home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / WWW / Perl_WWW_Utilities / MHonArc / mhonarc < prev    next >
Encoding:
Text File  |  1996-04-17  |  71.0 KB  |  2,334 lines

  1. #! /usr/local/bin/perl
  2. ##---------------------------------------------------------------------------##
  3. ##  File:
  4. ##      MHonArc
  5. ##  Author:
  6. ##      Earl Hood       ehood@convex.com
  7. ##  Contributers:
  8. ##    Steve Pacenka <sp17@cornell.edu>,
  9. ##    Achim Bohnet <ach@rosat.mpe-garching.mpg.de>,
  10. ##    Achille Petrilli <Achille.Petrilli@MACMAIL.CERN.CH>
  11. ##  Description:
  12. ##      MHonArc is a Perl program to convert mail to HTML.  See
  13. ##    accompany documentation for full details.
  14. ##---------------------------------------------------------------------------##
  15. ##    MHonArc -- Internet mail-to-HTML converter
  16. ##    Copyright (C) 1995,1996    Earl Hood, ehood@convex.com
  17. ##
  18. ##    This program is free software; you can redistribute it and/or modify
  19. ##    it under the terms of the GNU General Public License as published by
  20. ##    the Free Software Foundation; either version 2 of the License, or
  21. ##    (at your option) any later version.
  22. ##
  23. ##    This program is distributed in the hope that it will be useful,
  24. ##    but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. ##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26. ##    GNU General Public License for more details.
  27. ##
  28. ##    You should have received a copy of the GNU General Public License
  29. ##    along with this program; if not, write to the Free Software
  30. ##    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  31. ##---------------------------------------------------------------------------##
  32.  
  33. #############################################################################
  34. #############################################################################
  35. package main;
  36.  
  37. $VERSION = "1.2.2";        # Version number
  38.  
  39. ##---------------------------------------------------------------------------##
  40. ##                Main routine                     ##
  41. ##---------------------------------------------------------------------------##
  42.  
  43. &prestart();
  44. &get_cli_opts();
  45. &doit();
  46.  
  47. ##---------------------------------------------------------------------------##
  48. ##                              SubRoutines                                  ##
  49. ##---------------------------------------------------------------------------##
  50. ##---------------------------------------------------------------------------
  51. ##    prestart() does some initialization stuff.
  52. ##
  53. sub prestart {
  54.     ##    Turn off buffered I/O to terminal
  55.     select(STDOUT);  $| = 1;
  56.  
  57.     unshift(@INC, 'lib');    # Should I leave this line in?
  58.  
  59.     ##    Check what system we are executing under
  60.     require 'osinit.pl'       || &error("ERROR: Unable to require osinit.pl");
  61.     &'OSinit();
  62.  
  63.     ##    Require key libraries
  64.     require 'newgetopt.pl' || &error("ERROR: Unable to require newgetopt.pl");
  65.     require 'timelocal.pl' || &error("ERROR: Unable to require timelocal.pl");
  66.     require 'readmail.pl'  || &error("ERROR: Unable to require readmail.pl");
  67.     require 'mhdb.pl'      || &error("ERROR: Unable to require mhdb.pl");
  68.     require 'mhutil.pl'    || &error("ERROR: Unable to require mhutil.pl");
  69.  
  70.     ##    The %Zone array should be augmented to contain all timezone
  71.     ##    specifications with the positive/negative hour offset from UTC
  72.     ##    (GMT).  (There has got to be a better way to handle timezones)
  73.     %Zone = (
  74.     "UTC", 0,    # Universal Coordinated Time
  75.     "GMT", 0,    # Greenwich Mean Time
  76.     "AST", 4,    # Atlantic Standard Time
  77.     "ADT", 3,    # Atlantic Daylight Time
  78.     "EST", 5,    # Eastern Standard Time
  79.     "EDT", 4,    # Eastern Daylight Time
  80.     "CST", 6,    # Central Standard Time
  81.     "CDT", 5,    # Central Daylight Time
  82.     "MST", 7,    # Mountain Standard Time
  83.     "MDT", 6,    # Mountain Daylight Time
  84.     "PST", 8,    # Pacific Standard Time
  85.     "PDT", 7,    # Pacific Daylight Time
  86.     );
  87.     ##    Assoc array listing mail header fields to exclude in output.
  88.     ##    Each key is treated as a regular expression with '^' prepended
  89.     ##  to it.
  90.     %HFieldsExc = (
  91.     'content-', 1,        # Mime headers
  92.     'errors-to', 1,
  93.     'forward', 1,        # Forward lines (MH may add these)
  94.     'lines', 1,
  95.     'message-id', 1,
  96.     'mime-', 1,        # Mime headers
  97.     'nntp-', 1,
  98.     'originator', 1,
  99.     'path', 1,
  100.     'precedence', 1,
  101.     'received', 1,        # MTA added headers
  102.     'replied', 1,
  103.     'return-path', 1,    # MH creates this during inc
  104.     'status', 1,
  105.     'via', 1,
  106.     'x-', 1,        # Non-standard headers
  107.     );
  108.     ##    Asocc arrays defining HTML formats to apply to header fields
  109.     %HeadFields = (
  110.     "-default-", "",
  111.     );
  112.     %HeadHeads = (
  113.     "-default-", "em",
  114.     );
  115.     @FieldOrder = (
  116.     'to',
  117.     'subject',
  118.     'from',
  119.     'date',
  120.     '-extra-',
  121.     );
  122.     %FieldODefs = (
  123.     'to', 1,
  124.     'subject', 1,
  125.     'from', 1,
  126.     'date', 1,
  127.     );
  128.     $NumOfMsgs    =  0;    # Total number of messages
  129.     $LastMsgNum    = -1;    # Message number of last message
  130.     %Message      = ();    # Message bodies
  131.     %MsgHead      = ();    # Message heads
  132.     %MsgHtml      = ();    # Flag if message is html
  133.     %Subject      = ();    # Message subjects
  134.     %From      = ();    # Message froms
  135.     %Date      = ();    # Message dates
  136.     %MsgId      = ();    # Message Ids to indexes
  137.     %IndexNum     = ();    # Index key to message number
  138.     %Derived      = ();    # Derived files for messages
  139.     %Refs      = ();    # Message references
  140.     %Follow      = ();    # Message follow-ups
  141.     %FolCnt       = ();    # Number of follow-ups
  142.     %ContentType= ();    # Base content-type of messages
  143.     %Icons        = ();    # Icon URLs for content-types
  144.     %AddIndex     = ();    # Flags for messages that must be written
  145.     $bs     = "\b";
  146.     $Url     = '(http://|ftp://|afs://|wais://|telnet://|gopher://|' .
  147.                'news:|nntp:|mid:|cid:|mailto:|prospero:)';
  148.     $MLCP    = 0;
  149.     $ISLOCK    = 0;
  150.     $SLOW    = 0;
  151.  
  152.     ##    Get date
  153.     $curdate    = &getdate(0);
  154.     $locdate    = &getdate(1);
  155.  
  156.     ##    Set default filter libraries
  157.     @Requires = (
  158.  
  159.     "mhexternal.pl",
  160.     "mhtxthtml.pl",
  161.     "mhtxtplain.pl",
  162.     "mhtxtsetext.pl",
  163.  
  164.     );
  165.  
  166.     ##    Default filters
  167.     %MIMEFilters = (
  168.  
  169.     "application/mac-binhex40",    "m2h_external'filter",
  170.     "application/octet-stream",    "m2h_external'filter",
  171.     "application/oda",        "m2h_external'filter",
  172.     "application/pdf",        "m2h_external'filter",
  173.     "application/postscript",    "m2h_external'filter",
  174.     "application/rtf",        "m2h_external'filter",
  175.     "application/x-bcpio",        "m2h_external'filter",
  176.     "application/x-cpio",        "m2h_external'filter",
  177.     "application/x-csh",        "m2h_external'filter",
  178.     "application/x-dvi",        "m2h_external'filter",
  179.     "application/x-gtar",        "m2h_external'filter",
  180.     "application/x-hdf",        "m2h_external'filter",
  181.     "application/x-latex",        "m2h_external'filter",
  182.     "application/x-mif",        "m2h_external'filter",
  183.     "application/x-netcdf",        "m2h_external'filter",
  184.     "application/x-patch",        "m2h_text_plain'filter",
  185.     "application/x-sh",        "m2h_external'filter",
  186.     "application/x-shar",        "m2h_external'filter",
  187.     "application/x-sv4cpio",    "m2h_external'filter",
  188.     "application/x-sv4crc",        "m2h_external'filter",
  189.     "application/x-tar",        "m2h_external'filter",
  190.     "application/x-tcl",        "m2h_external'filter",
  191.     "application/x-tex",        "m2h_external'filter",
  192.     "application/x-texinfo",    "m2h_external'filter",
  193.     "application/x-troff",        "m2h_external'filter",
  194.     "application/x-troff-man",    "m2h_external'filter",
  195.     "application/x-troff-me",    "m2h_external'filter",
  196.     "application/x-troff-ms",    "m2h_external'filter",
  197.     "application/x-ustar",        "m2h_external'filter",
  198.     "application/x-wais-source",    "m2h_external'filter",
  199.     "application/zip",        "m2h_external'filter",
  200.     "audio/basic",            "m2h_external'filter",
  201.     "audio/x-aiff",            "m2h_external'filter",
  202.     "audio/x-wav",            "m2h_external'filter",
  203.     "image/gif",            "m2h_external'filter",
  204.     "image/ief",            "m2h_external'filter",
  205.     "image/jpeg",            "m2h_external'filter",
  206.     "image/tiff",            "m2h_external'filter",
  207.     "image/x-bmp",            "m2h_external'filter",
  208.     "image/x-cmu-raster",        "m2h_external'filter",
  209.     "image/x-pbm",            "m2h_external'filter",
  210.     "image/x-pcx",            "m2h_external'filter",
  211.     "image/x-pgm",            "m2h_external'filter",
  212.     "image/x-pict",            "m2h_external'filter",
  213.     "image/x-pnm",            "m2h_external'filter",
  214.     "image/x-portable-anymap",    "m2h_external'filter",
  215.     "image/x-portable-bitmap",    "m2h_external'filter",
  216.     "image/x-portable-graymap",    "m2h_external'filter",
  217.     "image/x-portable-pixmap",    "m2h_external'filter",
  218.     "image/x-ppm",            "m2h_external'filter",
  219.     "image/x-rgb",            "m2h_external'filter",
  220.     "image/x-xbitmap",        "m2h_external'filter",
  221.     "image/x-xbm",            "m2h_external'filter",
  222.     "image/x-xpixmap",        "m2h_external'filter",
  223.     "image/x-xpm",            "m2h_external'filter",
  224.     "image/x-xwd",            "m2h_external'filter",
  225.     "image/x-xwindowdump",        "m2h_external'filter",
  226.     "message/partial",        "m2h_text_plain'filter",
  227.     "text/html",            "m2h_text_html'filter",
  228.     "text/plain",            "m2h_text_plain'filter",
  229.     "text/richtext",        "m2h_text_plain'filter",
  230.     "text/setext",            "m2h_text_setext'filter",
  231.     "text/tab-separated-values",    "m2h_text_plain'filter",
  232.     "text/x-html",            "m2h_text_html'filter",
  233.     "text/x-setext",        "m2h_text_setext'filter",
  234.     "video/mpeg",            "m2h_external'filter",
  235.     "video/quicktime",        "m2h_external'filter",
  236.     "video/x-msvideo",        "m2h_external'filter",
  237.     "video/x-sgi-movie",        "m2h_external'filter",
  238.  
  239.     );
  240.  
  241.     ##  Default filter arguments
  242.     %MIMEFiltersArgs = (
  243.  
  244.     "image/gif",        "inline",
  245.     "image/x-xbitmap",    "inline",
  246.     "image/x-xbm",        "inline",
  247.     );
  248.  
  249.     ##    Grab environment variable settings
  250.     ##
  251.     $DBFILE    = ($ENV{'M2H_DBFILE'} ? $ENV{'M2H_DBFILE'} :
  252.           ($MSDOS ? "mhonarc.db" : ".mhonarc.db"));
  253.     $DOCURL    = ($ENV{'M2H_DOCURL'} ? $ENV{'M2H_DOCURL'} :
  254.           'http://www.oac.uci.edu/indiv/ehood/mhonarc.html');
  255.     $FOOTER    = ($ENV{'M2H_FOOTER'} ? $ENV{'M2H_FOOTER'} : "");
  256.     $HEADER    = ($ENV{'M2H_HEADER'} ? $ENV{'M2H_HEADER'} : "");
  257.     $IDXNAME   = ($ENV{'M2H_IDXFNAME'} ? $ENV{'M2H_IDXFNAME'} :
  258.           "maillist.html");
  259.     $IDXSIZE   = ($ENV{'M2H_IDXSIZE'} ? $ENV{'M2H_IDXSIZE'} : "");
  260.     $TIDXNAME  = ($ENV{'M2H_TIDXFNAME'} ? $ENV{'M2H_TIDXFNAME'} :
  261.           "threads.html");
  262.     $OUTDIR    = ($ENV{'M2H_OUTDIR'} ? $ENV{'M2H_OUTDIR'} : $CURDIR);
  263.     $FMTFILE   = ($ENV{'M2H_RCFILE'} ? $ENV{'M2H_RCFILE'} : "");
  264.     $TTITLE    = ($ENV{'M2H_TTITLE'} ? $ENV{'M2H_TTITLE'} :
  265.           "Mail Thread Index");
  266.     $TITLE     = ($ENV{'M2H_TITLE'} ? $ENV{'M2H_TITLE'} : "Mail Index");
  267.     $MAILTOURL = ($ENV{'M2H_MAILTOURL'} ? $ENV{'M2H_MAILTOURL'} : "");
  268.     $FROM      = ($ENV{'M2H_MSGSEP'} ? $ENV{'M2H_MSGSEP'} : '^From ');
  269.     $LOCKFILE  = ($ENV{'M2H_LOCKFILE'} ? $ENV{'M2H_LOCKFILE'} : 
  270.           ($MSDOS ? "mhonarc.lck" : ".mhonarc.lck"));
  271.     $LOCKTRIES = ($ENV{'M2H_LOCKTRIES'} ? $ENV{'M2H_LOCKTRIES'} : 10);
  272.     $LOCKDELAY = ($ENV{'M2H_LOCKDELAY'} ? $ENV{'M2H_LOCKDELAY'} : 3);
  273.     $MAXSIZE   = ($ENV{'M2H_MAXSIZE'} ? $ENV{'M2H_MAXSIZE'} : "");
  274.     $THREAD    = (defined($ENV{'M2H_THREAD'}) ? $ENV{'M2H_THREAD'} : 1);
  275.     $TLEVELS   = ($ENV{'M2H_TLEVELS'} ? $ENV{'M2H_TLEVELS'} : 3);
  276.  
  277.     $LIBEG      = '';        # List open template for main index
  278.     $LIEND      = '';        # List close template for main index
  279.     $LITMPL     = '';        # List item template
  280.     $TFOOT    = '';        # Thread index footer
  281.     $THEAD    = '';        # Thread index header
  282.     $TLITXT    = '';        # Thread index list item template
  283.  
  284.     $MSGFOOT    = '';        # Message footer
  285.     $MSGHEAD    = '';        # Message header
  286.     $TOPLINKS    = '';        # Message links at top of message
  287.     $BOTLINKS    = '';        # Message links at bottom of message
  288.     $NEXTBUTTON        = '';    # Next button template
  289.     $NEXTBUTTONIA    = '';    # Next inactive button template
  290.     $PREVBUTTON        = '';    # Previous button template
  291.     $PREVBUTTONIA    = '';    # Previous inactive button template
  292.     $NEXTLINK        = '';    # Next link template
  293.     $NEXTLINKIA        = '';    # Next inactive link template
  294.     $PREVLINK        = '';    # Previous link template
  295.     $PREVLINKIA        = '';    # Previous inactive link template
  296.  
  297.     $IDXPGBEG    = '';        # Beginning of main index page
  298.     $IDXPGEND    = '';        # Ending of main index page
  299.     $TIDXPGBEG    = '';        # Beginning of thread index page
  300.     $TIDXPGEND    = '';        # Ending of thread index page
  301.  
  302.     $MSGPGBEG    = '';        # Beginning of message page
  303.     $MSGPGEND    = '';        # Ending of message page
  304.  
  305.     # $PREVBL    = '[Prev]';    # No longer used
  306.     # $NEXTBL    = '[Next]';    # No longer used
  307.     # $IDXBL    = '[Index]';    # No longer used
  308.     # $TIDXBL    = '[Thread]';    # No longer used
  309.  
  310.     # $PREVFL    = 'Prev';    # No longer used
  311.     # $NEXTFL    = 'Next';    # No longer used
  312.     # $IDXFL    = 'Index';    # No longer used
  313.     # $TIDXFL    = 'Thread';    # No longer used
  314.  
  315.     ##    Init some flags
  316.     ##
  317.     $NOSORT   = 0;  $REVSORT  = 0;  $NONEWS  = 0;  $TREVERSE  = 0;
  318.     $NOMAILTO = 0;  $NOURL    = 0;  $SUBSORT = 0;  $NODOC     = 0;
  319.     $TSUBSORT = 0;
  320.     $UMASK = sprintf("%o",umask)  if $UNIX;
  321.  
  322.     $X = "\034";    # Value separator (should equal $;)
  323.             # NOTE: Older versions used this variable for
  324.             #    the multiple field separator in parsed
  325.             #    message headers.  $'FieldSep should
  326.             #    now be used (readmail.pl).
  327. }
  328. ##---------------------------------------------------------------------------
  329. ##    get_cli_opts() is responsible for grabbing command-line options
  330. ##    and also settings the resource file.
  331. ##
  332. sub get_cli_opts {
  333.     local($tmp, @array);
  334.  
  335.     &error(qq{Try "$PROG -help" for usage information}) unless
  336.     &NGetOpt(
  337.     "add",        # Add a message to archive
  338.     "dbfile=s",    # Database/state filename for mhonarc archive
  339.     "docurl=s",    # URL to mhonarc documentation
  340.     "editidx",    # Change index page layout only
  341.     "footer=s",    # File containing user text for bottom of index page
  342.     "force",    # Perform archive operation even if unable to lock
  343.     "genidx",    # Generate an index based upon archive contents
  344.     "header=s",    # File containing user text for top of index page
  345.     "idxfname=s",    # File name of index page
  346.     "idxsize=i",    # Maximum number of messages shown in indexes
  347.     "lockdelay=i",    # Time delay in seconds between lock tries
  348.     "locktries=i",    # Number of tries in locking an archive
  349.     "mailtourl=s",    # URL to use for e-mail address hyperlinks
  350.     "maxsize=i",    # Maximum number of messages allowed in archive
  351.     "mbox",        # Use mailbox format        (ignored now)
  352.     "mh",        # Use MH mail folders format    (ignored now)
  353.     "msgsep=s",    # Message separator for mailbox files
  354.     "nodoc",    # Do not print link to doc at end of index page
  355.     "nomailto",    # Do not add in mailto links for e-mail addresses
  356.     "nonews",    # Do not add links to newsgroups
  357.     "noreverse",    # List messages in normal order
  358.     "nosort",    # Do not sort
  359.     "nothread",    # Do not create threaded index
  360.     "notreverse",    # List oldest thread first
  361.     "notsubsort",    # Do sort listed threads by subject; sort by date
  362.     "nourl",    # Do not make URL hyperlinks
  363.     "outdir=s",    # Destination of HTML files
  364.     "quiet",    # No status messages while running
  365.     "rcfile=s",    # Resource file for mhonarc
  366.     "reverse",    # List messages in reverse order
  367.     "revsort",    # Perform reverse sorting on dates
  368.     "rmm",        # Remove messages from an archive
  369.     "savemem",    # Write message data while processing
  370.     "scan",        # List out archive contents to terminal
  371.     "single",    # Convert a single message to HTML
  372.     "sort",        # Sort messages in increasing date order
  373.     "subsort",    # Sort message by subject
  374.     "tidxfname=s",    # File name of threaded index page
  375.     "time",        # Print processing time
  376.     "title=s",    # Title of index page
  377.     "ttitle=s",    # Title of threaded index page
  378.     "thread",    # Create threaded index
  379.     "tlevels=i",    # Maximum # of nested lists in threaded index
  380.     "treverse",    # List most recent thread first
  381.     "tsubsort",    # Sort listed threads by subject
  382.     "umask=i",    # Set umask of process
  383.  
  384.     "help"        # A brief usage message
  385.     );
  386.     &usage() if defined($opt_help);
  387.  
  388.     ## These options have NO resource file equivalent.
  389.     ##
  390.     $ADD     = defined($opt_add);
  391.     $RMM     = defined($opt_rmm);
  392.     $SCAN    = defined($opt_scan);
  393.     $QUIET   = defined($opt_quiet);
  394.     $EDITIDX = defined($opt_editidx);
  395.     if (defined($opt_genidx)) {
  396.     $IDXONLY  = 1;  $QUIET = 1;
  397.     } else {
  398.     $IDXONLY  = 0;
  399.     }
  400.     if (defined($opt_single)) {
  401.     $SINGLE  = 1;  $QUIET = 1;
  402.     } else {
  403.     $SINGLE = 0;
  404.     }
  405.     &usage() unless ($#ARGV >= 0) || $ADD || $SINGLE ||
  406.             $EDITIDX || $SCAN || $IDXONLY;
  407.     $FMTFILE = $opt_rcfile   if $opt_rcfile;
  408.     $LOCKTRIES = $opt_locktries  if ($opt_locktries > 0);
  409.     $LOCKDELAY = $opt_lockdelay  if ($opt_lockdelay > 0);
  410.     $FORCELOCK = defined($opt_force);
  411.  
  412.     ## These options must be grabbed before reading the database file
  413.     ## since these options may tells us where the database file is.
  414.     ##
  415.     $OUTDIR  = $opt_outdir    if $opt_outdir;
  416.     if (!(-r $OUTDIR) || !(-w $OUTDIR) || !(-x $OUTDIR)) {
  417.         &error("ERROR: Unable to access $OUTDIR");
  418.     }
  419.     $DBFILE  = $opt_dbfile    if $opt_dbfile;
  420.  
  421.     ## Create lockfile
  422.     ##
  423.     $LOCKFILE  = "${OUTDIR}${DIRSEP}${LOCKFILE}";
  424.     if (!$SINGLE && !&create_lock_file($LOCKFILE, 1, 0, 0)) {
  425.     print STDOUT "Trying to lock mail archive in $OUTDIR ...\n"
  426.         unless $QUIET;
  427.     if (!&create_lock_file($LOCKFILE,
  428.                    $LOCKTRIES-1,
  429.                    $LOCKDELAY,
  430.                    $FORCELOCK)) {
  431.         &error("ERROR: Unable to create $LOCKFILE after $LOCKTRIES tries");
  432.     }
  433.     }
  434.     ## Race condition exists: if process is terminated before termination
  435.     ## handlers set, lock file will not get removed.
  436.     ##
  437.     &set_handler();
  438.  
  439.     ## Check if we need to access database file
  440.     ##
  441.     if ($ADD || $EDITIDX || $RMM || $SCAN || $IDXONLY) {
  442.     $DBFILE = ".mail2html.db"
  443.         unless (-e "${OUTDIR}${DIRSEP}${DBFILE}") ||
  444.            (!-e "${OUTDIR}${DIRSEP}.mail2html.db");
  445.     if (-e "${OUTDIR}${DIRSEP}${DBFILE}") {
  446.         eval qq%require "${OUTDIR}${DIRSEP}${DBFILE}"%;
  447.         &error("ERROR: Database read error of ",
  448.            "${OUTDIR}${DIRSEP}${DBFILE}:\n\t$@")  if $@;
  449.         $OldNOSORT   = $NOSORT;
  450.         $OldSUBSORT  = $SUBSORT;
  451.         $OldREVSORT     = $REVSORT;
  452.         if ($VERSION ne $DbVERSION) {
  453.         warn "Warning: Database ($DbVERSION) != ",
  454.              "program ($VERSION) version.\n";
  455.         }
  456.     }
  457.     if ($#ARGV < 0) { $ADDSINGLE = 1; }    # See if adding single mesg
  458.     else { $ADDSINGLE = 0; }
  459.     $ADD = 'STDIN';
  460.     }
  461.     $OldTITLE = $TITLE;
  462.     $OldTHREAD = $THREAD;
  463.     $OldTTITLE = $TTITLE;
  464.  
  465.     ## Get highest message number
  466.     if ($ADD) {
  467.     $LastMsgNum = &get_last_msg_num();
  468.     } else {
  469.     $LastMsgNum = -1;
  470.     }
  471.  
  472.     ## Remove lock file if scanning messages
  473.     ##
  474.     if ($SCAN) {
  475.     &clean_up();
  476.     }
  477.  
  478.     ##    Read resource file (I initially used the term 'format file').
  479.     ##    Look for resource in outdir if not absolute path or not
  480.     ##    existing according to current value.
  481.     ##
  482.     if ($FMTFILE) {
  483.     $FMTFILE = "${OUTDIR}${DIRSEP}$FMTFILE"
  484.         unless ($FMTFILE =~ m%^/%) || (-e $FMTFILE);
  485.     &read_fmt_file($FMTFILE);
  486.     }
  487.  
  488.     ## Require MIME filters and other libraries
  489.     ##
  490.     unshift(@INC, @PerlINC);
  491.     if (!$EDITIDX && !$SCAN && !$RMM) {
  492.     &remove_dups(*Requires);
  493.     print STDOUT "Requiring MIME filter libraries ...\n"  unless $QUIET;
  494.     foreach (@Requires) {
  495.         print STDOUT "\t$_\n"  unless $QUIET;
  496.         eval qq{require "$_"};
  497.         &error("ERROR: Unable to require ${_}:\n\t$@")  if $@;
  498.     }
  499.     ## Register message header formatter to readmail library
  500.     $readmail'FormatHeaderFunc = "main'htmlize_header";
  501.     }
  502.  
  503.     ## Get other command-line options
  504.     ##
  505.     $DBFILE    = $opt_dbfile     if $opt_dbfile; # Set again to override db
  506.     $DOCURL    = $opt_docurl     if $opt_docurl;
  507.     $FOOTER    = $opt_footer     if $opt_footer;
  508.     $FROM    = $opt_msgsep     if $opt_msgsep;
  509.     $HEADER    = $opt_header     if $opt_header;
  510.     $IDXNAME    = $opt_idxfname   if $opt_idxfname;
  511.     $IDXSIZE    = $opt_idxsize    if $opt_idxsize;
  512.     $IDXSIZE *= -1  if $IDXSIZE < 0;
  513.     $OUTDIR    = $opt_outdir     if $opt_outdir; # Set again to override db
  514.     $MAILTOURL    = $opt_mailtourl  if $opt_mailtourl;
  515.     $MAXSIZE    = $opt_maxsize    if $opt_maxsize;
  516.     $MAXSIZE = ""  if $MAXSIZE < 0;
  517.     $TIDXNAME    = $opt_tidxfname  if $opt_tidxfname;
  518.     $TITLE    = $opt_title      if $opt_title;
  519.     $TLEVELS    = $opt_tlevels    if $opt_tlevels;
  520.     $TTITLE    = $opt_ttitle     if $opt_ttitle;
  521.  
  522.     $NODOC    = 1  if defined($opt_nodoc);
  523.     $NOMAILTO    = 1  if defined($opt_nomailto);
  524.     $NONEWS    = 1  if defined($opt_nonews);
  525.     $NOURL    = 1  if defined($opt_nourl);
  526.     $SLOW    = 1  if defined($opt_savemem);
  527.     $THREAD    = 1  if defined($opt_thread);
  528.     $THREAD    = 0  if defined($opt_nothread);
  529.     $TREVERSE    = 1  if defined($opt_treverse);
  530.     $TREVERSE    = 0  if defined($opt_notreverse);
  531.     $TSUBSORT    = 1  if defined($opt_tsubsort);
  532.     $TSUBSORT    = 0  if defined($opt_notsubsort);
  533.  
  534.     ##    Set umask
  535.     if ($UNIX) {
  536.     $UMASK = $opt_umask      if $opt_umask;
  537.     eval 'umask oct($UMASK)';
  538.     }
  539.  
  540.     ##    Get sort method
  541.     ##
  542.     $SORTCHNG = 0;
  543.     if (defined($opt_nosort)) {        # No sorting takes highest precedence
  544.     $NOSORT = 1;  $SUBSORT = 0;
  545.     } elsif (defined($opt_subsort)) {    # Subject sort
  546.     $SUBSORT = 1;  $NOSORT = 0;
  547.     } elsif (defined($opt_sort)) {    # Regular sort is last
  548.     $NOSORT = 0;  $SUBSORT = 0;
  549.     }
  550.     ## Check for listing order
  551.     ##
  552.     if (defined($opt_noreverse)) {
  553.     $REVSORT = 0;
  554.     } elsif (defined($opt_reverse) || defined($opt_revsort)) {
  555.     $REVSORT = 1;
  556.     }
  557.     $SORTCHNG = 1  if (($OldNOSORT != $NOSORT) ||
  558.                ($OldSUBSORT != $SUBSORT) ||
  559.                ($OldREVSORT != $REVSORT));
  560.  
  561.     ## Check if all messages must be updated
  562.     ##
  563.     if ($SORTCHNG || $RMM || $EDITIDX ||
  564.     ($OldTITLE ne $TITLE) ||
  565.     ($OldTTITLE ne $TTITLE) ||
  566.     ($THREAD != $OldTHREAD)) {
  567.     $UPDATE_ALL = 1;
  568.     } else {
  569.     $UPDATE_ALL = 0;
  570.     }
  571.  
  572.     ##    Check index resources
  573.     $IDXPGBEG = join('',
  574.              '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',
  575.              "\n",
  576.              "<HTML>\n",
  577.              "<HEAD>\n",
  578.              '<TITLE>$IDXTITLE$</TITLE>', "\n",
  579.              "</HEAD>\n",
  580.              "<BODY>\n",
  581.              '<H1>$IDXTITLE$</H1>', "\n")
  582.         unless $IDXPGBEG;
  583.     $IDXPGEND = join('',
  584.              "</BODY>\n",
  585.              "</HTML>\n")
  586.         unless $IDXPGEND;
  587.  
  588.     if ($THREAD) {
  589.     $LIBEG  = join('', "<UL>\n",
  590.                '<LI><A HREF="$TIDXFNAME$">Thread Index</A></LI>',
  591.                "\n",
  592.                "</UL>\n<HR>\n<UL>\n")
  593.             unless $LIBEG;
  594.     $THEAD  = join('', "<UL>\n",
  595.                '<LI><A HREF="$IDXFNAME$">Main Index</A></LI>',
  596.                "\n",
  597.                "</UL>\n<HR>\n")
  598.             unless $THEAD;
  599.     $TLITXT = '($NUMFOLUP$) <STRONG>$SUBJECT:40$</STRONG>, ' .
  600.           '<EM>$FROMNAME$</EM>'
  601.             unless $TLITXT;
  602.     $TIDXPGBEG = join('',
  603.              "<!DOCTYPE HTML PUBLIC ",
  604.              qq{"-//IETF//DTD HTML 2.0//EN">\n},
  605.              "<HTML>\n",
  606.              "<HEAD>\n",
  607.              '<TITLE>$TIDXTITLE$</TITLE>', "\n",
  608.              "</HEAD>\n",
  609.              "<BODY>\n",
  610.              '<H1>$TIDXTITLE$</H1>', "\n")
  611.             unless $TIDXPGBEG;
  612.     $TIDXPGEND = join('',
  613.              "</BODY>\n",
  614.              "</HTML>\n")
  615.             unless $TIDXPGEND;
  616.  
  617.     } else {
  618.     $LIBEG = "<HR>\n<UL>\n"  unless $LIBEG;
  619.     }
  620.     $LIEND  = "</UL>\n"
  621.         unless $LIEND;
  622.     $LITMPL = join('', '<LI><STRONG>$SUBJECT$</STRONG>', "\n",
  623.                '<UL><LI><EM>From</EM>: $FROM$</LI></UL>' , "\n",
  624.                "</LI>\n")
  625.         unless $LITMPL;
  626.  
  627.     ##    Message resources
  628.     $MSGPGBEG = join('',
  629.              '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',
  630.              "\n",
  631.              "<HTML>\n",
  632.              "<HEAD>\n",
  633.              '<TITLE>$SUBJECTNA:72$</TITLE>', "\n",
  634.              '<LINK REV="made" HREF="mailto:$FROMADDR$">', "\n",
  635.              "</HEAD>\n",
  636.              "<BODY>\n")
  637.         unless $MSGPGBEG;
  638.     $MSGPGEND = join('',
  639.              "</BODY>\n",
  640.              "</HTML>\n")
  641.         unless $MSGPGEND;
  642.  
  643.     #     Check for next/prev message link resources.  Must check for
  644.     #     older variables to preserve capatibility.
  645.     $PREVBL    = '[Prev]'    unless $PREVBL;
  646.     $NEXTBL    = '[Next]'    unless $NEXTBL;
  647.     $IDXBL    = '[Index]'    unless $IDXBL;
  648.     $TIDXBL    = '[Thread]'    unless $TIDXBL;
  649.     $NEXTFL    = 'Next'    unless $NEXTFL;
  650.     $PREVFL    = 'Prev'    unless $PREVFL;
  651.  
  652.     $NEXTBUTTON = '<A HREF="$NEXTMSG$">' . $NEXTBL . '</A>'
  653.     unless $NEXTBUTTON;
  654.     $PREVBUTTON = '<A HREF="$PREVMSG$">' . $PREVBL . '</A>'
  655.     unless $PREVBUTTON;
  656.     $NEXTBUTTONIA = ''
  657.     unless $PREVBUTTONIA;
  658.     $PREVBUTTONIA = ''
  659.     unless $PREVBUTTONIA;
  660.  
  661.     $NEXTLINK    = join('',
  662.                "<LI>$NEXTFL: <STRONG>",
  663.                '<A HREF="$NEXTMSG$">$NEXTSUBJECT$</A>',
  664.                "</STRONG></LI>\n")  unless $NEXTLINK;
  665.     $NEXTLINKIA = ''  unless $NEXTLINKIA;
  666.     $PREVLINK    = join('',
  667.                "<LI>$PREVFL: <STRONG>",
  668.                '<A HREF="$PREVMSG$">$PREVSUBJECT$</A>',
  669.                "</STRONG></LI>\n")  unless $PREVLINK;
  670.     $PREVLINKIA = ''  unless $PREVLINKIA;
  671.  
  672.     if (!$TOPLINKS) {
  673.     $TOPLINKS  = join('',
  674.               "<HR>\n",
  675.                   '$PREVBUTTON$$NEXTBUTTON$',
  676.                   '<A HREF="$IDXFNAME$#$MSGNUM$">', $IDXBL, '</A>');
  677.     $TOPLINKS .= join('',
  678.               '<A HREF="$TIDXFNAME$#$MSGNUM$">', $TIDXBL, '</A>')
  679.              if $THREAD;
  680.     }
  681.  
  682.     if (!$BOTLINKS) {
  683.     $BOTLINKS =  join('',
  684.               "<HR>\n",
  685.               "<UL>\n",
  686.               '$PREVLINK$',
  687.               '$NEXTLINK$',
  688.               "<LI>Index(es):\n",
  689.               "<UL>\n",
  690.               '<LI><A HREF="$IDXFNAME$#$MSGNUM$">',
  691.               "<STRONG>Main</STRONG></A></LI>\n");
  692.     $BOTLINKS .= join('',
  693.               '<LI><A HREF="$TIDXFNAME$#$MSGNUM$">',
  694.               "<STRONG>Thread</STRONG></A></LI>\n")
  695.              if $THREAD;
  696.     $BOTLINKS .= "</UL>\n</LI>\n</UL>\n";
  697.     }
  698.  
  699.     ##    Set unknown icon
  700.     $Icons{'unknown'} = $Icons{'text/plain'}  unless $Icons{'unknown'};
  701.  
  702.     ##    Set some other variables
  703.     $IDXPATHNAME    = "${OUTDIR}${DIRSEP}${IDXNAME}";
  704.     $TIDXPATHNAME    = "${OUTDIR}${DIRSEP}${TIDXNAME}";
  705.  
  706.     ##  Create dynamic subroutines.
  707.     &create_routines();
  708.  
  709.     $TIME = defined($opt_time);
  710.     $StartTime = (times)[0]  if ($TIME);
  711. }
  712. ##---------------------------------------------------------------------------
  713. sub doit {
  714.     ## Check for non-archive modification modes.
  715.     if ($SCAN) {
  716.     &scan();
  717.     &quit(0);
  718.     } elsif ($SINGLE) {
  719.     &single();
  720.     &quit(0);
  721.     }
  722.  
  723.     ## Following causes changes to an archive
  724.     local($mesg, $tmp, $index, $sub, $from, $i, $date, @array,
  725.       @array2, $tmp2, %fields);
  726.  
  727.     $i = $NumOfMsgs;
  728.     ##-------------------##
  729.     ## Read mail folders ##
  730.     ##-------------------##
  731.     if ($EDITIDX || $IDXONLY) {
  732.     print STDOUT "Editing $OUTDIR layout ...\n"  unless $QUIET;
  733.  
  734.     } elsif ($RMM) {        ## Delete messages
  735.     print STDOUT "Removing messages from $OUTDIR ...\n"
  736.         unless $QUIET;
  737.     &rmm(*ARGV);
  738.  
  739.     } elsif ($ADDSINGLE) {        ## Adding single message
  740.     print STDOUT "Adding message to $OUTDIR\n"  unless $QUIET;
  741.     $handle = $ADD;
  742.  
  743.     ## Read mail head
  744.     ($index,$from,$date,$sub,$header) =
  745.         &read_mail_header($handle, *mesg, *fields);
  746.  
  747.     if ($index ne '') {
  748.         ($From{$index},$Date{$index},$Subject{$index}) =
  749.         ($from,$date,$sub);
  750.  
  751.         $AddIndex{$index} = 1;
  752.         $IndexNum{$index} = &getNewMsgNum();
  753.  
  754.         $MsgHead{$index} = $mesg;
  755.         $MsgHead{$index} .= "<HR>\n"  unless $mesg =~ /^\s*$/;
  756.  
  757.         ## Read rest of message
  758.         $Message{$index} = &read_mail_body(
  759.                     $handle,
  760.                     $index,
  761.                     $header,
  762.                         *fields);
  763.     }
  764.  
  765.     } else {            ## Adding/converting mail{boxes,folders}
  766.     print STDOUT ($ADD ? "Adding" : "Converting"), " messages to $OUTDIR"
  767.         unless $QUIET;
  768.     local($mbox, $mesgfile, @files);
  769.     foreach $mbox (@ARGV) {
  770.         if (-d $mbox) {        # MH mail folder
  771.         if (!opendir(MAILDIR, $mbox)) {
  772.             warn "\nWarning: Unable to open $mbox\n";
  773.             next;
  774.         }
  775.         $MBOX = 0;  $MH = 1;
  776.         print STDOUT "\nReading $mbox "  unless $QUIET;
  777.         @files = sort numerically grep(/^\d+$/, readdir(MAILDIR));
  778.         closedir(MAILDIR);
  779.         foreach (@files) {
  780.             $mesgfile = "${mbox}${DIRSEP}${_}";
  781.             if (!open(FILE, $mesgfile)) {
  782.             warn "\nWarning: Unable to open message $mesgfile\n";
  783.             next;
  784.             }
  785.             print STDOUT "."  unless $QUIET;
  786.             $mesg = '';
  787.             ($index,$from,$date,$sub,$header) =
  788.             &read_mail_header(FILE, *mesg, *fields);
  789.  
  790.             #  Process message if valid
  791.             if ($index ne '') {
  792.             ($From{$index},$Date{$index},$Subject{$index}) =
  793.                 ($from,$date,$sub);
  794.             $MsgHead{$index} = $mesg;
  795.             $MsgHead{$index} .= "<HR>\n"  unless $mesg =~ /^\s*$/;
  796.  
  797.             if ($ADD && !$SLOW) { $AddIndex{$index} = 1; }
  798.             $IndexNum{$index} = &getNewMsgNum();
  799.  
  800.             $Message{$index} = &read_mail_body(
  801.                         FILE,
  802.                         $index,
  803.                         $header,
  804.                             *fields);
  805.             #  Check if conserving memory
  806.             if ($SLOW) {
  807.                 &output_mail($index, 0, 0, *bogus, 1, 1);
  808.                 $Update{$IndexNum{$index}} = 1;
  809.                 undef $MsgHead{$index};
  810.                 undef $Message{$index};
  811.             }
  812.             }
  813.             close(FILE);
  814.         }
  815.         } else {        # UUCP mail box file
  816.         if (!open(FILE, $mbox)) {
  817.             warn "\nWarning: Unable to open $mbox\n";
  818.             next;
  819.         }
  820.         $MBOX = 1;  $MH = 0;
  821.         print STDOUT "\nReading $mbox "  unless $QUIET;
  822.         while (<FILE>) { last if /$FROM/o; }
  823.         MBOX: while (!eof(FILE)) {
  824.             print STDOUT "."  unless $QUIET;
  825.             $mesg = '';
  826.             ($index,$from,$date,$sub,$header) =
  827.             &read_mail_header(FILE, *mesg, *fields);
  828.  
  829.             if ($index ne '') {
  830.             ($From{$index},$Date{$index},$Subject{$index}) =
  831.                 ($from,$date,$sub);
  832.             $MsgHead{$index} = $mesg;
  833.             $MsgHead{$index} .= "<HR>\n"  unless $mesg =~ /^\s*$/;
  834.  
  835.             if ($ADD && !$SLOW) { $AddIndex{$index} = 1; }
  836.             $IndexNum{$index} = &getNewMsgNum();
  837.  
  838.             $Message{$index} = &read_mail_body(
  839.                         FILE,
  840.                         $index,
  841.                         $header,
  842.                         *fields);
  843.             if ($SLOW) {
  844.                 &output_mail($index, 0, 0, *bogus, 1, 1);
  845.                 $Update{$IndexNum{$index}} = 1;
  846.                 undef $MsgHead{$index};
  847.                 undef $Message{$index};
  848.             }
  849.             } else {
  850.             &read_mail_body(FILE, $index, $header, *fields, 1);
  851.             }
  852.         }
  853.         close(FILE);
  854.         }
  855.     }
  856.     }
  857.  
  858.     ## Check if there are any new messages
  859.     if (!$EDITIDX && !$IDXONLY && $i == $NumOfMsgs) {
  860.     print STDOUT "\nNo new messages\n"  unless $QUIET;
  861.     &quit(0);
  862.     }
  863.  
  864.     ##---------------------------------------------##
  865.     ## Setup data structures for final HTML output ##
  866.     ##---------------------------------------------##
  867.  
  868.     ## Remove old message if hit maximum size
  869.     if (!$IDXONLY && $MAXSIZE && ($NumOfMsgs > $MAXSIZE)) {
  870.     if ($REVSORT) {
  871.         @array = reverse &sort_messages();
  872.     } else {
  873.         @array = &sort_messages();
  874.     }
  875.     &ign_signals();                # Ignore termination signals
  876.     while ($NumOfMsgs > $MAXSIZE) {
  877.         $index = shift @array;
  878.         &delmsg($index);
  879.         $Update{$IndexNum{$array[0]}} = 1;          # Update next
  880.         foreach (split(/$bs/o, $FollowOld{$index})) { # Update any replies
  881.         $Update{$IndexNum{$_}} = 1;
  882.         }
  883.     }
  884.     }
  885.     @array = &sort_messages();
  886.  
  887.     ## Compute follow up messages
  888.     foreach $index (@array) {
  889.     $FolCnt{$index} = 0  unless $FolCnt{$index};
  890.     if (@array2 = split(/$'X/o, $Refs{$index})) {
  891.         $tmp2 = $array2[$#array2];
  892.         next unless defined($IndexNum{$MsgId{$tmp2}});
  893.         $tmp = $MsgId{$tmp2};
  894.         if ($Follow{$tmp}) { $Follow{$tmp} .= $bs . $index; }
  895.         else { $Follow{$tmp} = $index; }
  896.         $FolCnt{$tmp}++;
  897.     }
  898.     }
  899.  
  900.     ## Check for which messages to update when adding to archive
  901.     if (!$IDXONLY && $ADD) {
  902.     if ($UPDATE_ALL) {
  903.         foreach $index (@array) { $Update{$IndexNum{$index}} = 1; }
  904.     } else {
  905.         $i = 0;
  906.         foreach $index (@array) {
  907.         ## Check for New follow-up links
  908.         if ($FollowOld{$index} ne $Follow{$index}) {
  909.             $Update{$IndexNum{$index}} = 1;
  910.         }
  911.         ## Check if new message; must update links in prev/next mesgs
  912.         if ($AddIndex{$index}) {
  913.             $Update{$IndexNum{$array[$i-1]}} = 1  if $i > 0;
  914.             $Update{$IndexNum{$array[$i+1]}} = 1  if $i < $#array;
  915.         }
  916.         ## Check for New reference links
  917.         foreach (split(/$'X/o, $Refs{$index})) {
  918.             $tmp = $MsgId{$_};
  919.             if (defined($IndexNum{$tmp}) && $AddIndex{$tmp}) {
  920.             $Update{$IndexNum{$index}} = 1;
  921.             }
  922.         }
  923.         $i++;
  924.         }
  925.     }
  926.     }
  927.  
  928.     ##------------##
  929.     ## Write HTML ##
  930.     ##------------##
  931.     &ign_signals();                # Ignore termination signals
  932.     print STDOUT "\n"  unless $QUIET;
  933.     if (!$IDXONLY) {
  934.     &write_mail(*array);
  935.     &write_main_index();
  936.     &write_thread_index()  if $THREAD;
  937.     } elsif ($THREAD) {
  938.     &write_thread_index();
  939.     } else {
  940.     &write_main_index();
  941.     }
  942.  
  943.     ## Save archive state
  944.     if (!$IDXONLY) {
  945.     &output_db();
  946.     foreach $tmp (@OtherIdxs) {
  947.         $THREAD = 0;
  948.         $tmp = "${OUTDIR}${DIRSEP}$tmp"
  949.         unless ($tmp =~ m%^/%) || (-e $tmp);
  950.         if (&read_fmt_file($tmp)) {
  951.         if ($THREAD) {
  952.             $TIDXPATHNAME = "${OUTDIR}${DIRSEP}${TIDXNAME}";
  953.             &write_thread_index();
  954.         } else {
  955.             $IDXPATHNAME = "${OUTDIR}${DIRSEP}${IDXNAME}";
  956.             &write_main_index();
  957.         }
  958.         }
  959.     }
  960.     print STDOUT "$NumOfMsgs messages\n"  unless $QUIET;
  961.     }
  962.  
  963.     &quit(0);
  964. }
  965. ##---------------------------------------------------------------------------
  966. ##    Function to do scan feature.
  967. ##
  968. sub scan {
  969.     local($key, $num, $index, $day, $mon, $year, $from, $date,
  970.       $subject, $time, @array);
  971.  
  972.     print STDOUT "$NumOfMsgs messages in $OUTDIR:\n\n";
  973.     print STDOUT sprintf("%5s  %s  %-15s  %-45s\n",
  974.              "Msg #", "YY/MM/DD", "From", "Subject");
  975.     print STDOUT sprintf("%5s  %s  %-15s  %-45s\n",
  976.              "-" x 5, "--------", "-" x 15, "-" x 45);
  977.  
  978.     @array = &sort_messages();
  979.     foreach $index (@array) {
  980.     $date = &time2mmddyy((split(/$X/o, $index))[0], 'yymmdd');
  981.     $num = $IndexNum{$index};
  982.     $from = substr(&dehtmlize(&extract_email_name($From{$index})), 0, 15);
  983.     $subject = substr(&dehtmlize($Subject{$index}), 0, 45);
  984.     print STDOUT sprintf("%5d  %s  %-15s  %-45s\n",
  985.                  $num, $date, $from, $subject);
  986.     }
  987. }
  988. ##---------------------------------------------------------------------------
  989. ##    Routine to perform conversion of a single mail message to
  990. ##    HTML.
  991. ##
  992. sub single {
  993.     local($mhead,$index,$from,$date,$sub,$header,$handle,$mesg,
  994.       $template,$filename,%fields);
  995.  
  996.     ## Prevent any verbose output
  997.     $QUIET = 1;
  998.  
  999.     ## See where input is coming from
  1000.     if ($ARGV[0]) {
  1001.     open(SINGLE, $ARGV[0]) || &error("ERROR: Unable to open $ARGV[0]");
  1002.     $handle = 'SINGLE';
  1003.     $filename = $ARGV[0];
  1004.     } else {
  1005.     $handle = 'STDIN';
  1006.     }
  1007.  
  1008.     ## Read header
  1009.     ($index,$from,$date,$sub,$header) =
  1010.     &read_mail_header($handle, *mhead, *fields);
  1011.  
  1012.     ($From{$index},$Date{$index},$Subject{$index}) = ($from,$date,$sub);
  1013.  
  1014.     ## Read rest of message
  1015.     $mesg = &read_mail_body($handle, $index, $header, *fields);
  1016.  
  1017.     ## Output to stdout
  1018.     $template = $MSGPGBEG;
  1019.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1020.     print STDOUT $template;
  1021.  
  1022.     $template = $MSGHEAD;
  1023.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1024.     print STDOUT $template;
  1025.  
  1026.     print STDOUT "<H1>$sub</H1>\n",
  1027.          "<HR>\n",
  1028.          $mhead;
  1029.  
  1030.     print STDOUT "<HR>\n"  unless $mhead =~ /^\s*$/;
  1031.     print STDOUT $mesg,
  1032.          "<HR>\n";
  1033.  
  1034.     $template = $MSGFOOT;
  1035.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1036.     print STDOUT $template;
  1037.  
  1038.     $template = $MSGPGEND;
  1039.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1040.     print STDOUT $template;
  1041. }
  1042. ##---------------------------------------------------------------------------
  1043. ##    Function for removing messages.  *numbers points to an array
  1044. ##    of message numbers to delete
  1045. ##
  1046. sub rmm {
  1047.     local(*numbers) = shift;
  1048.     local($key, %Num2Index, $num, $didrmm, $filename);
  1049.  
  1050.     if ($#numbers < 0) {
  1051.     &error("Error: No message numbers specified");
  1052.     }
  1053.     $didrmm = 0;
  1054.  
  1055.     ## Make assoc arrays to perform deletions
  1056.     foreach $key (keys %IndexNum) {
  1057.     $Num2Index{$IndexNum{$key}} = $key;
  1058.     }
  1059.     ## Remove messages
  1060.     foreach $num (@numbers) {
  1061.     if ($num !~ /^\d+$/) {
  1062.         print STDERR "`$num' is not a legal message number\n";
  1063.     }
  1064.     if ($key = $Num2Index{$num}) {
  1065.         print STDOUT "\tRemoving message $num\n"  unless $QUIET;
  1066.         &delmsg($key);
  1067.         $didrmm = 1;
  1068.     } else {
  1069.         print STDOUT "\tMessage $num does not exist\n"  unless $QUIET;
  1070.     }
  1071.     }
  1072.     if (!$didrmm) {
  1073.     &error("ERROR: Messages specified do not exist");
  1074.     }
  1075. }
  1076. ##---------------------------------------------------------------------------
  1077. sub delmsg {
  1078.     local($key) = @_;
  1079.     local($filename);
  1080.  
  1081.     &defineIndex2MsgId();
  1082.     $msgnum = $IndexNum{$key};  return 0  if ($msgnum eq '');
  1083.     $filename = $OUTDIR . $DIRSEP . &msgnum_filename($msgnum);
  1084.     delete $ContentType{$key};
  1085.     delete $Date{$key};
  1086.     delete $From{$key};
  1087.     delete $IndexNum{$key};
  1088.     delete $Refs{$key};
  1089.     delete $Subject{$key};
  1090.     delete $MsgId{$Index2MsgId{$key}};
  1091.     unlink $filename;
  1092.     foreach $filename (split(/$'X/o, $Derived{$key})) {
  1093.     unlink "${OUTDIR}${DIRSEP}${filename}";
  1094.     }
  1095.     delete $Derived{$key};
  1096.     $NumOfMsgs--;
  1097.     1;
  1098. }
  1099. ##---------------------------------------------------------------------------
  1100. ##    write_mail outputs converted mail.  It takes a reference to an
  1101. ##    array containing indexes of messages to output.
  1102. ##
  1103. sub write_mail {
  1104.     local(*idxarray) = $_[0];
  1105.     local($max, $hack) = ($#idxarray, 0);
  1106.     print STDOUT "Writing mail ...\n"  unless $QUIET;
  1107.     if ($SLOW && !$ADD) {  $ADD = 1;  $hack = 1; }
  1108.     $i = 0;
  1109.     foreach $index (@idxarray) {
  1110.     &output_mail($index, $i, $max, *idxarray, $AddIndex{$index}, 0);
  1111.     $i++;
  1112.     }
  1113.     if ($hack) { $ADD = 0; }
  1114. }
  1115. ##---------------------------------------------------------------------------
  1116. ##    write_main_index outputs main index of archive
  1117. ##
  1118. sub write_main_index {
  1119.     local(@array) = &sort_messages();
  1120.     local($outhandle, $i, $i_p0, $filename, $tmpl);
  1121.  
  1122.     ## Set messages that are shown in index
  1123.     if ($IDXSIZE && (($i = ($#array+1) - $IDXSIZE) > 0)) {
  1124.     if ($REVSORT) {
  1125.         splice(@array, $IDXSIZE);
  1126.     } else {
  1127.         splice(@array, 0, $i);
  1128.     }
  1129.     }
  1130.  
  1131.     ## Open/create index file
  1132.     if ($ADD) {
  1133.     if (-e $IDXPATHNAME) {
  1134.         &cp($IDXPATHNAME, "${OUTDIR}${DIRSEP}tmp.$$");
  1135.         open(MAILLISTIN, "${OUTDIR}${DIRSEP}tmp.$$")
  1136.         || &error("ERROR: Unable to open ${OUTDIR}${DIRSEP}tmp.$$");
  1137.         $MLCP = 1;
  1138.     } else {
  1139.         $MLCP = 0;
  1140.     }
  1141.     }
  1142.     if ($IDXONLY) {
  1143.        $outhandle = STDOUT;
  1144.     } else {
  1145.     open(MAILLIST, "> $IDXPATHNAME") ||
  1146.         &error("ERROR: Unable to create $IDXPATHNAME");
  1147.     $outhandle = 'MAILLIST';
  1148.     }
  1149.     print STDOUT "Writing $IDXPATHNAME ...\n"  unless $QUIET;
  1150.  
  1151.     ## Print top part of index
  1152.     &output_maillist_head($outhandle, MAILLISTIN);
  1153.  
  1154.     ## Output messages to HTML
  1155.     $i = 0;
  1156.     foreach $index (@array) {
  1157.     $msgnum = $IndexNum{$index};
  1158.     $i_p0 = &fmt_msgnum($msgnum);        # Var for replace_li_var
  1159.     $filename = &msgnum_filename($msgnum);    # Var for replace_li_var
  1160.     $tmpl = $LITMPL;
  1161.     $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1162.     print $outhandle $tmpl;
  1163.     $i++;
  1164.     }
  1165.  
  1166.     ## Print bottom part of index
  1167.     &output_maillist_foot($outhandle, MAILLISTIN);
  1168.     close($outhandle)  unless $IDXONLY;
  1169.     close(MAILLISTIN), unlink("${OUTDIR}${DIRSEP}tmp.$$")  if $MLCP;
  1170. }
  1171. ##---------------------------------------------------------------------------
  1172. ##    write_thread_index outputs the thread index
  1173. ##
  1174. sub write_thread_index {
  1175.     local($tmpl, $handle);
  1176.  
  1177.     if ($IDXONLY) {
  1178.     $handle = 'STDOUT';
  1179.     } else {
  1180.     open(THREAD, "> $TIDXPATHNAME") ||
  1181.         &error("ERROR: Unable to create $TIDXPATHNAME");
  1182.     $handle = 'THREAD';
  1183.     }
  1184.     print STDOUT "Writing $TIDXPATHNAME ...\n"  unless $QUIET;
  1185.  
  1186.     $tmpl = $TIDXPGBEG;
  1187.     $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1188.     print $handle $tmpl;
  1189.  
  1190.     $tmpl = $THEAD;
  1191.     $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1192.     print $handle $tmpl;
  1193.  
  1194.     &output_thread_index($handle);
  1195.  
  1196.     $tmpl = $TFOOT;
  1197.     $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1198.     print $handle $tmpl;
  1199.  
  1200.     &output_doclink($handle);
  1201.  
  1202.     $tmpl = $TIDXPGEND;
  1203.     $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1204.     print $handle $tmpl;
  1205.  
  1206.     close($handle)  unless $IDXONLY;
  1207. }
  1208. ##---------------------------------------------------------------------------
  1209. ##    read_mail_header() is responsible for parsing the header of
  1210. ##    a mail message.
  1211. ##
  1212. sub read_mail_header {
  1213.     local($handle, *mesg, *fields) = @_;
  1214.     local(%l2o, $header, $index, $from, $sub, $date, $tmp, $msgid,
  1215.       @refs, @array);
  1216.  
  1217.     $header = &'MAILread_file_header("main'$handle", *fields, *l2o);
  1218.  
  1219.     ##------------##
  1220.     ## Get Msg-ID ##
  1221.     ##------------##
  1222.     $msgid = $fields{'message-id'} || $fields{'msg-id'} || 
  1223.          $fields{'content-id'};
  1224.     if (!($msgid =~ s/\s*<([^>]*)>\s*/$1/g)) {
  1225.     $msgid =~ s/^\s*//;
  1226.     $msgid =~ s/\s*$//;
  1227.     }
  1228.  
  1229.     #    Return if message already exists in archive
  1230.     #
  1231.     if ($msgid && defined($MsgId{$msgid})) {
  1232.     return ("", "", "", "", "");
  1233.     }
  1234.  
  1235.     ##----------##
  1236.     ## Get date ##
  1237.     ##----------##
  1238.     $date = '';
  1239.     if ($fields{'received'}) {
  1240.     @array = split(/$'FieldSep/o, $fields{'received'});
  1241.     $tmp = shift @array;
  1242.     @array = split(/;/, $tmp);
  1243.     $date = pop @array;
  1244.     } elsif ($fields{'date'}) {
  1245.     @array = split(/$'FieldSep/o, $fields{'date'});
  1246.     $date = shift @array;
  1247.     }
  1248.     if ($date =~ /\w/) {
  1249.     local($wday, $mday, $mon, $yr, $hr, $min, $sec, $zone) =
  1250.         &parse_date($date);
  1251.     if ($zone) {
  1252.         $index = &timegm($sec,$min,$hr,$mday,$mon,
  1253.                  ($yr > 1900 ? $yr-1900 : $yr));
  1254.     } else {
  1255.         $index = &timelocal($sec,$min,$hr,$mday,$mon,
  1256.                 ($yr > 1900 ? $yr-1900 : $yr));
  1257.     }
  1258.  
  1259.     ## Try to modify time/date based on timezone ##
  1260.     if ($zone =~ /^[\+-]\d+$/) {# Numeric timezone
  1261.         $zone =~ s/0//g;
  1262.         $index -= ($zone*3600);
  1263.     } else {                                # Timezone abbrev
  1264.         warn qq|Warning: Undefined time zone: "$zone", Line $.\n|
  1265.         if $zone && !defined($Zone{$zone});
  1266.         $index += ($Zone{$zone}*3600);      # %Zone defined above
  1267.     }
  1268.     } else {
  1269.     warn "Warning: Could not find date for message\n";
  1270.     $date = '';  $index = 0;
  1271.     }
  1272.     ##-------------##
  1273.     ## Get Subject ##
  1274.     ##-------------##
  1275.     if ($fields{'subject'} !~ /^\s*$/) {
  1276.     ($sub = $fields{'subject'}) =~ s/\s*$//;
  1277.     &htmlize(*sub);
  1278.     } else {
  1279.     $sub = 'No Subject';
  1280.     }
  1281.     ##----------##
  1282.     ## Get From ##
  1283.     ##----------##
  1284.     $tmp = $fields{'from'} || $fields{'apparently-from'};
  1285.     $from = &convert_line($tmp);
  1286.     ##----------------##
  1287.     ## Get References ##
  1288.     ##----------------##
  1289.     $tmp = $fields{'references'};
  1290.     while ($tmp =~ s/<([^>]+)>//) {
  1291.     push(@refs, $1);
  1292.     }
  1293.     $tmp = $fields{'in-reply-to'};
  1294.     if ($tmp =~ s/^[^<]*<([^>]*)>.*$/$1/) {
  1295.     push(@refs, $tmp)  unless $tmp =~ /^\s*$/;
  1296.     }
  1297.     ##------------------------##
  1298.     ## Create HTML for header ##
  1299.     ##------------------------##
  1300.     $mesg .= &htmlize_header(*fields, *l2o);
  1301.  
  1302.     ## Insure uniqueness of msg-id
  1303.     $index .= $'X . sprintf("%d",$LastMsgNum+1);
  1304.  
  1305.     if ($fields{'content-type'}) {
  1306.     ($tmp = $fields{'content-type'}) =~ m%^\s*([\w-/]+)%;
  1307.     $tmp = $1 || 'text/plain';
  1308.     $tmp =~ tr/A-Z/a-z/;
  1309.     } else {
  1310.     $tmp = 'text/plain';
  1311.     }
  1312.     $ContentType{$index} = $tmp;
  1313.  
  1314.     $MsgId{$msgid} = $index;
  1315.     &remove_dups(*refs);                # Remove duplicate msg-ids
  1316.     $Refs{$index} = join($'X, @refs)  if (@refs);
  1317.  
  1318.     ($index,$from,$date,$sub,$header);
  1319. }
  1320. ##---------------------------------------------------------------------------
  1321. ##    read_mail_body() reads in the body of a message.  The returned
  1322. ##    filtered body is in $ret.
  1323. ##
  1324. sub read_mail_body {
  1325.     local($handle, $index, $header, *fields, $skip) = @_;
  1326.     local($ret, $data, @files);
  1327.  
  1328.     while (<$handle>) {
  1329.     last  if $MBOX && /$FROM/o;
  1330.     $data .= $_;
  1331.     }
  1332.     return ''  if $skip;
  1333.     $fields{'content-type'} = 'text/plain'
  1334.     if $fields{'content-type'} =~ /^\s*$/;
  1335.     ($ret, @files) = &'MAILread_body($header, $data,
  1336.                     $fields{'content-type'},
  1337.                     $fields{'content-transfer-encoding'});
  1338.     $ret = join('',
  1339.         "<DL>\n",
  1340.         "<DT><STRONG>Warning</STRONG></DT>\n",
  1341.         "<DD>Could not process message with given Content-Type: \n",
  1342.         "<CODE>", $fields{'content-type'}, "</CODE>\n",
  1343.         "</DD>\n",
  1344.         "</DL>\n"
  1345.         )  unless $ret;
  1346.     if (@files) {
  1347.     $Derived{$index} = join($'X, @files);
  1348.     }
  1349.     $ret;
  1350. }
  1351. ##---------------------------------------------------------------------------
  1352. ##    Output/edit a mail message.
  1353. ##        $index    => current index (== $array[$i])
  1354. ##        $i        => current index into *array
  1355. ##        $maxnum    => size of *array
  1356. ##        *array    => reference to array of indexes
  1357. ##        $force    => flag if mail is written and not editted, regardless
  1358. ##        $nocustom    => ignore sections with user customization
  1359. ##               ($i, $maxnum, *array ignored if true)
  1360. ##
  1361. sub output_mail {
  1362.     local($index, $i, $maxnum, *array, $force, $nocustom) = @_;
  1363.     local($msgi,$tmp,$tmp2,$template,@array2);
  1364.     local($filepathname, $tmppathname);
  1365.     local($adding) = ($ADD && !$force);
  1366.  
  1367.     # Variables for replace_li_var
  1368.     local($i_p0,$i_p1,$i_m1,$filename,$nextindex,$previndex);
  1369.  
  1370.     if (!$nocustom) {
  1371.     $nextindex = $array[$i+1];
  1372.     $previndex = $array[$i-1];
  1373.     }
  1374.  
  1375.     # Here $i is the current message count and not necessarily the
  1376.     # message number in the filename.
  1377.  
  1378.     $i_p0 = &fmt_msgnum($IndexNum{$index});
  1379.     if (!$nocustom) {
  1380.     $i_p1 = &fmt_msgnum($IndexNum{$nextindex});
  1381.     $i_m1 = &fmt_msgnum($IndexNum{$previndex});
  1382.     }
  1383.  
  1384.     $filename = &msgnum_filename($IndexNum{$index});
  1385.     $filepathname = $OUTDIR . $DIRSEP . $filename;
  1386.     $tmppathname = $OUTDIR . $DIRSEP . "msgtmp.$$";
  1387.  
  1388.     if ($adding) {
  1389.     return ($i_p0,$filename)  unless $Update{$IndexNum{$index}};
  1390.     &cp($filepathname, $tmppathname);
  1391.     open(MSGFILEIN, $tmppathname)
  1392.         || &error("ERROR: Unable to open $tmppathname");
  1393.     }
  1394.     open(MSGFILE, "> $filepathname")
  1395.     || &error("ERROR: Unable to create $filepathname");
  1396.  
  1397.     ## Output HTML header
  1398.     if ($adding) {
  1399.     while (<MSGFILEIN>) { last  if /<!--X-Body-Begin/; }
  1400.     }
  1401.     if (!$nocustom) {
  1402.     &defineIndex2MsgId();
  1403.     print MSGFILE "<!--X-Subject: $Subject{$index} -->\n",
  1404.               "<!--X-From: $From{$index} -->\n",
  1405.               "<!--X-Date: $Date{$index} -->\n",
  1406.               "<!--X-Message-Id: $Index2MsgId{$index} -->\n",
  1407.               "<!--X-ContentType: $ContentType{$index} -->\n",
  1408.               "<!--X-Head-End-->\n";
  1409.     $template = $MSGPGBEG;
  1410.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1411.     print MSGFILE $template;
  1412.     }
  1413.     print MSGFILE "<!--X-Body-Begin-->\n";
  1414.  
  1415.     ## Output header
  1416.     if ($adding) {
  1417.     while (<MSGFILEIN>) {
  1418.         last  if /<!--X-User-Header-End/ || /<!--X-TopPNI--/;
  1419.     }
  1420.     }
  1421.     print MSGFILE "<!--X-User-Header-->\n";
  1422.     if (!$nocustom) {
  1423.     $template = $MSGHEAD;
  1424.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1425.     print MSGFILE $template;
  1426.     }
  1427.     print MSGFILE "<!--X-User-Header-End-->\n";
  1428.  
  1429.     ## Output Prev/Next/Index links at top
  1430.     if ($adding) {
  1431.     while (<MSGFILEIN>) { last  if /<!--X-TopPNI-End/; }
  1432.     }
  1433.     print MSGFILE "<!--X-TopPNI-->\n";
  1434.     if (!$nocustom) {
  1435.     $template = $TOPLINKS;
  1436.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1437.     print MSGFILE $template;
  1438.     }
  1439.     print MSGFILE qq|\n|;
  1440.     print MSGFILE "<!--X-TopPNI-End-->\n";
  1441.  
  1442.     ## Output message body
  1443.     if ($adding) {
  1444.     $tmp2 = '';
  1445.     while (<MSGFILEIN>) {
  1446.         $tmp2 .= $_;
  1447.         last  if /<!--X-MsgBody-End/;
  1448.     }
  1449.     foreach (split(/$'X/o, $Refs{$index})) {# Convert msg-ids to hyperlinks
  1450.         ($tmp = $_) =~ s/(\W)/\\$1/g;
  1451.         if (defined($IndexNum{$MsgId{$_}}) &&
  1452.         $IndexNum{$MsgId{$_}} != $IndexNum{$index}) {
  1453.         $msgi = &fmt_msgnum($IndexNum{$MsgId{$_}});
  1454.         $tmp2 =~ s/$tmp/<A HREF="msg$msgi.html">$_<\/A>/g;
  1455.         }
  1456.     }
  1457.     print MSGFILE $tmp2;
  1458.     } else {
  1459.     print MSGFILE "<!--X-MsgBody-->\n";
  1460.     print MSGFILE "<H1>", $Subject{$index}, "</H1>\n";
  1461.     print MSGFILE "<HR>\n";
  1462.     foreach (split(/$'X/o, $Refs{$index})) {# Convert msg-ids to hyperlinks
  1463.         ($tmp = $_) =~ s/(\W)/\\$1/g;
  1464.         if (defined($IndexNum{$MsgId{$_}}) &&
  1465.         $IndexNum{$MsgId{$_}} != $IndexNum{$index}) {
  1466.  
  1467.         $msgi = &fmt_msgnum($IndexNum{$MsgId{$_}});
  1468.         $MsgHead{$index} =~
  1469.             s/$tmp/<A HREF="msg$msgi.html">$_<\/A>/g;
  1470.         $Message{$index} =~
  1471.             s/$tmp/<A HREF="msg$msgi.html">$_<\/A>/g;
  1472.         }
  1473.     }
  1474.  
  1475.     print MSGFILE $MsgHead{$index};
  1476.     print MSGFILE $Message{$index};
  1477.     print MSGFILE "<!--X-MsgBody-End-->\n";
  1478.     }
  1479.  
  1480.     ## Output any followup messages
  1481.     if ($adding) {
  1482.     while (<MSGFILEIN>) { last  if /<!--X-Follow-Ups-End/; }
  1483.     }
  1484.     print MSGFILE "<!--X-Follow-Ups-->\n";
  1485.     if (!$nocustom) {
  1486.     @array2 = split(/$bs/o, $Follow{$index});
  1487.     if ($#array2 >= 0) {
  1488.         $tmp = 1;        # Here, $tmp a flag if <HR> printed
  1489.         print MSGFILE "<HR>\n",
  1490.                "<STRONG>Follow-Ups</STRONG>:\n",
  1491.                "<UL>\n";
  1492.         foreach (@array2) {
  1493.         print MSGFILE "<LI>",
  1494.                qq|<STRONG><A HREF="|, &msgnum_filename($IndexNum{$_}),
  1495.                qq|">$Subject{$_}</A></STRONG></LI>\n|,
  1496.                "<UL>\n",
  1497.                "<LI><EM>From</EM>: $From{$_}</LI>\n",
  1498.                "</UL>\n";
  1499.         }
  1500.         print MSGFILE "</UL>\n";
  1501.     } else {
  1502.         $tmp = 0;
  1503.     }
  1504.     }
  1505.     print MSGFILE "<!--X-Follow-Ups-End-->\n";
  1506.  
  1507.     ## Output any references
  1508.     if ($adding) {
  1509.     while (<MSGFILEIN>) { last  if /<!--X-References-End/; }
  1510.     }
  1511.     print MSGFILE "<!--X-References-->\n";
  1512.     if (!$nocustom) {
  1513.     @array2 = split(/$'X/o, $Refs{$index});  $tmp2 = 0;
  1514.     if ($#array2 >= 0) {
  1515.         foreach (@array2) {
  1516.         if (defined($IndexNum{$MsgId{$_}})) {
  1517.             if (!$tmp) { print MSGFILE "<HR>\n"; $tmp = 1; }
  1518.             if (!$tmp2) {
  1519.             print MSGFILE "<STRONG>References</STRONG>:\n",
  1520.                    "<UL>\n";
  1521.             $tmp2 = 1;
  1522.             }
  1523.             print MSGFILE "<LI>",
  1524.                qq|<STRONG><A HREF="|,
  1525.                &msgnum_filename($IndexNum{$MsgId{$_}}),
  1526.                qq|">$Subject{$MsgId{$_}}</A></STRONG></LI>\n|,
  1527.                "<UL>\n",
  1528.                "<LI><EM>From</EM>: $From{$MsgId{$_}}</LI>\n",
  1529.                "</UL>\n";
  1530.         }
  1531.         }
  1532.         print MSGFILE "</UL>\n"  if $tmp2;
  1533.     }
  1534.     }
  1535.     print MSGFILE "<!--X-References-End-->\n";
  1536.  
  1537.     ## Output verbose links to prev/next message in list
  1538.     if ($adding) {
  1539.     while (<MSGFILEIN>) { last  if /<!--X-BotPNI-End/; }
  1540.     }
  1541.     print MSGFILE "<!--X-BotPNI-->\n";
  1542.     if (!$nocustom) {
  1543.     $template = $BOTLINKS;
  1544.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1545.     print MSGFILE $template;
  1546.     print MSGFILE qq|\n|;
  1547.     }
  1548.     print MSGFILE "<!--X-BotPNI-End-->\n";
  1549.  
  1550.     ## Output footer
  1551.     if ($adding) {
  1552.     while (<MSGFILEIN>) {
  1553.         last  if /<!--X-User-Footer-End/;
  1554.     }
  1555.     }
  1556.     print MSGFILE "<!--X-User-Footer-->\n";
  1557.     if (!$nocustom) {
  1558.     $template = $MSGFOOT;
  1559.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1560.     print MSGFILE $template;
  1561.     }
  1562.     print MSGFILE "<!--X-User-Footer-End-->\n";
  1563.  
  1564.     if (!$nocustom) {
  1565.     $template = $MSGPGEND;
  1566.     $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1567.     print MSGFILE $template;
  1568.     }
  1569.  
  1570.     close(MSGFILE);
  1571.     close(MSGFILEIN), unlink($tmppathname)  if ($adding);
  1572.  
  1573.     ($i_p0, $filename);
  1574. }
  1575. ##---------------------------------------------------------------------------
  1576. ##    output_maillist_head() outputs the beginning of the index page.
  1577. ##
  1578. sub output_maillist_head {
  1579.     local($handle, $cphandle) = @_;
  1580.     local($tmp);
  1581.  
  1582.     ## Output title
  1583.     $tmp = $IDXPGBEG;
  1584.     $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1585.     print $handle $tmp;
  1586.     print $handle "<!--X-ML-Title-H1-End-->\n";
  1587.  
  1588.     if ($MLCP) {
  1589.     while (<$cphandle>) { last  if /<!--X-ML-Title-H1-End/; }
  1590.     }
  1591.  
  1592.     ## Output header file
  1593.     if ($HEADER) {                # Read external header
  1594.     print $handle "<!--X-ML-Header-->\n";
  1595.     if (open(HEADER, $HEADER)) {
  1596.         print $handle <HEADER>;
  1597.     } else {
  1598.         warn "Warning: Unable to open header: $HEADER\n";
  1599.     }
  1600.     if ($MLCP) {
  1601.         while (<$cphandle>) { last  if /<!--X-ML-Header-End/; }
  1602.     }
  1603.     print $handle "<!--X-ML-Header-End-->\n";
  1604.     } elsif ($MLCP) {                # Preserve maillist header
  1605.     while (<$cphandle>) {
  1606.         print $handle $_;
  1607.         last  if /<!--X-ML-Header-End/;
  1608.     }
  1609.     } else {                    # No header
  1610.     print $handle "<!--X-ML-Header-->\n",
  1611.               "<!--X-ML-Header-End-->\n";
  1612.     }
  1613.  
  1614.     print $handle "<!--X-ML-Index-->\n";
  1615.     $tmp = $LIBEG;
  1616.     $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1617.     print $handle $tmp;
  1618. }
  1619. ##---------------------------------------------------------------------------
  1620. ##    output_maillist_foot() outputs the end of the index page.
  1621. ##
  1622. sub output_maillist_foot {
  1623.     local($handle, $cphandle) = @_;
  1624.     local($tmp);
  1625.  
  1626.     $tmp = $LIEND;
  1627.     $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1628.     print $handle $tmp;
  1629.     print $handle "<!--X-ML-Index-End-->\n";
  1630.  
  1631.     ## Skip past index in old maillist file
  1632.     if ($MLCP) {
  1633.     while (<$cphandle>) { last  if /<!--X-ML-Index-End/; }
  1634.     }
  1635.  
  1636.     ## Output footer file
  1637.     if ($FOOTER) {                # Read external footer
  1638.     print $handle "<!--X-ML-Footer-->\n";
  1639.     if (open(FOOTER, $FOOTER)) {
  1640.         print $handle <FOOTER>;
  1641.     } else {
  1642.         warn "Warning: Unable to open footer: $FOOTER\n";
  1643.     }
  1644.     if ($MLCP) {
  1645.         while (<$cphandle>) { last  if /<!--X-ML-Footer-End/; }
  1646.     }
  1647.     print $handle "<!--X-ML-Footer-End-->\n";
  1648.     } elsif ($MLCP) {                # Preserve maillist footer
  1649.     while (<$cphandle>) {
  1650.         print $handle $_;
  1651.         last  if /<!--X-ML-Footer-End/;
  1652.     }
  1653.     } else {                    # No footer
  1654.     print $handle "<!--X-ML-Footer-->\n",
  1655.               "<!--X-ML-Footer-End-->\n";
  1656.     }
  1657.  
  1658.     &output_doclink($handle);
  1659.  
  1660.     ## Close document
  1661.     $tmp = $IDXPGEND;
  1662.     $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1663.     print $handle $tmp;
  1664. }
  1665. ##---------------------------------------------------------------------------
  1666. ##    Output link to documentation, if specified
  1667. ##
  1668. sub output_doclink {
  1669.     local($handle) = ($_[0]);
  1670.     if (!$NODOC && $DOCURL) {
  1671.     print $handle "<HR>\n";
  1672.     print $handle
  1673.         "<ADDRESS>\n",
  1674.         "Mail converted by ",
  1675.         qq|<A HREF="$DOCURL"><CODE>MHonArc</CODE></A> $VERSION\n|,
  1676.         "</ADDRESS>\n";
  1677.     }
  1678. }
  1679. #############################################################################
  1680. ## Miscellaneous routines
  1681. #############################################################################
  1682. ##---------------------------------------------------------------------------
  1683. sub getNewMsgNum {
  1684.     $NumOfMsgs++; $LastMsgNum++;
  1685.     $LastMsgNum;
  1686. }
  1687. ##---------------------------------------------------------------------------
  1688. ##    replace_li_var() is used to substitute vars to current
  1689. ##    values.  This routine relies on dynamic linking for $i,
  1690. ##    $i_{p0,p1,m1}, $index, $maxnum and $filename.
  1691. ##
  1692. sub replace_li_var {
  1693.     local($val) = $_[0];
  1694.     local($var,$len,$canclip,$raw,$isurl,$tmp,$ret) = ('',0,0,0,0,'','');
  1695.     local($expand) = (0);
  1696.  
  1697.     ##    Get length specifier (if defined)
  1698.     ($var, $len) = split(/:/, $val, 2);
  1699.  
  1700.     ##    Check if variable in a URL string
  1701.     $isurl = 1  if ($len =~ s/u//ig);    
  1702.  
  1703.     REPLACESW: {
  1704.     if ($var eq 'SUBJECT') {
  1705.         $canclip = 1; $raw = 1; $isurl = 0;
  1706.         $tmp = &dehtmlize($Subject{$index});
  1707.         last REPLACESW;
  1708.     }
  1709.         if ($var eq 'SUBJECTNA') {
  1710.         $canclip = 1; $raw = 1;
  1711.         $tmp = &dehtmlize($Subject{$index});
  1712.         last REPLACESW;
  1713.     }
  1714.         if ($var eq 'A_ATTR') {
  1715.         $isurl = 0; $tmp = qq|NAME="$i_p0" HREF="$filename"|;
  1716.         last REPLACESW;
  1717.     }
  1718.         if ($var eq 'A_NAME')
  1719.         { $isurl = 0; $tmp = qq|NAME="$i_p0"|; last REPLACESW; }
  1720.         if ($var eq 'A_HREF')
  1721.         { $isurl = 0; $tmp = qq|HREF="$filename"|; last REPLACESW; }
  1722.         if ($var eq 'DATE')
  1723.         { $tmp = $Date{$index}; last REPLACESW; }
  1724.         if ($var eq 'DDMMYY') {
  1725.         $tmp = &time2mmddyy((split(/$X/o, $index))[0], 'ddmmyy');
  1726.         last REPLACESW;
  1727.     }
  1728.         if ($var eq 'DOCURL')
  1729.         { $isurl = 0; $tmp = $DOCURL; last REPLACESW; }
  1730.         if ($var eq 'FROM') {
  1731.         $canclip = 1; $raw = 1;
  1732.         $tmp = &dehtmlize($From{$index});
  1733.         last REPLACESW;
  1734.     }
  1735.         if ($var eq 'FROMADDR') {
  1736.         $canclip = 1; $raw = 1;
  1737.         $tmp = &dehtmlize(&extract_email_address($From{$index}));
  1738.         last REPLACESW;
  1739.     }
  1740.         if ($var eq 'FROMNAME') {
  1741.         $canclip = 1; $raw = 1;
  1742.         $tmp = &dehtmlize(&extract_email_name($From{$index}));
  1743.         last REPLACESW;
  1744.     }
  1745.         if ($var eq 'GMTDATE')
  1746.         { $tmp = $curdate; last REPLACESW; }
  1747.         if ($var eq 'ICON') {
  1748.         if ($Icons{$ContentType{$index}}) {
  1749.         $tmp = qq|<IMG SRC="$Icons{$ContentType{$index}}" | .
  1750.                qq|ALT="[$ContentType{$index}]">|;
  1751.         } else {
  1752.         $tmp = qq|<IMG SRC="$Icons{'unknown'}" ALT="[unknown]">|;
  1753.         }
  1754.         last REPLACESW;
  1755.     }
  1756.         if ($var eq 'ICONURL') {
  1757.         $isurl = 0;
  1758.         if ($Icons{$ContentType{$index}}) {
  1759.         $tmp = $Icons{$ContentType{$index}};
  1760.         } else {
  1761.         $tmp = $Icons{'unknown'};
  1762.         }
  1763.         last REPLACESW;
  1764.     }
  1765.         if ($var eq 'IDXFNAME')
  1766.         { $tmp = $IDXNAME; last REPLACESW; }
  1767.         if ($var eq 'IDXSIZE')
  1768.         { $tmp = $IDXSIZE; last REPLACESW; }
  1769.         if ($var eq 'IDXTITLE')
  1770.         { $canclip = 1; $tmp = $TITLE; last REPLACESW; }
  1771.         if ($var eq 'LOCALDATE')
  1772.         { $tmp = $locdate; last REPLACESW; }
  1773.         if ($var eq 'MMDDYY') {
  1774.         $tmp = &time2mmddyy((split(/$X/o, $index))[0], 'mmddyy');
  1775.         last REPLACESW;
  1776.     }
  1777.         if ($var eq 'MSGID') {
  1778.         &defineIndex2MsgId();
  1779.         $tmp = $Index2MsgId{$index};
  1780.         last REPLACESW;
  1781.     }
  1782.         if ($var eq 'MSGNUM')
  1783.         { $tmp = $i_p0; last REPLACESW; }
  1784.         if ($var eq 'NEXTFROM') {
  1785.         $canclip = 1; $raw = 1;
  1786.         $tmp = &dehtmlize($From{$nextindex});
  1787.         last REPLACESW;
  1788.     }
  1789.         if ($var eq 'NEXTFROMADDR') {
  1790.         $canclip = 1; $raw = 1;
  1791.         $tmp = &dehtmlize(&extract_email_address($From{$nextindex}));
  1792.         last REPLACESW;
  1793.     }
  1794.         if ($var eq 'NEXTFROMNAME') {
  1795.         $canclip = 1; $raw = 1;
  1796.         $tmp = &dehtmlize(&extract_email_name($From{$nextindex}));
  1797.         last REPLACESW;
  1798.     }
  1799.         if ($var eq 'NEXTMSG')
  1800.         { $tmp = "msg${i_p1}.html"; last REPLACESW; }
  1801.         if ($var eq 'NEXTMSGNUM')
  1802.         { $tmp = $i_p1; last REPLACESW; }
  1803.     if ($var eq 'NEXTSUBJECT') {
  1804.         $canclip = 1; $raw = 1;
  1805.         $tmp = &dehtmlize($Subject{$nextindex});
  1806.         last REPLACESW;
  1807.     }
  1808.         if ($var eq 'NUMFOLUP')
  1809.         { $tmp = $FolCnt{$index}; last REPLACESW; }
  1810.         if ($var eq 'NUMOFIDXMSG') {
  1811.         $tmp = ($NumOfMsgs > $IDXSIZE ? $IDXSIZE : $NumOfMsgs);
  1812.         last REPLACESW;
  1813.     }
  1814.         if ($var eq 'NUMOFMSG')
  1815.         { $tmp = $NumOfMsgs; last REPLACESW; }
  1816.         if ($var eq 'ORDNUM')
  1817.         { $tmp = $i+1; last REPLACESW; }
  1818.         if ($var eq 'OUTDIR')
  1819.         { $tmp = $OUTDIR; last REPLACESW; }
  1820.         if ($var eq 'PREVFROM') {
  1821.         $canclip = 1; $raw = 1;
  1822.         $tmp = &dehtmlize($From{$previndex});
  1823.         last REPLACESW;
  1824.     }
  1825.         if ($var eq 'PREVFROMADDR') {
  1826.         $canclip = 1; $raw = 1;
  1827.         $tmp = &dehtmlize(&extract_email_address($From{$previndex}));
  1828.         last REPLACESW;
  1829.     }
  1830.         if ($var eq 'PREVFROMNAME') {
  1831.         $canclip = 1; $raw = 1;
  1832.         $tmp = &dehtmlize(&extract_email_name($From{$previndex}));
  1833.         last REPLACESW;
  1834.     }
  1835.         if ($var eq 'PREVMSG')
  1836.         { $tmp = "msg${i_m1}.html"; last REPLACESW; }
  1837.         if ($var eq 'PREVMSGNUM')
  1838.         { $tmp = $i_m1; last REPLACESW; }
  1839.     if ($var eq 'PREVSUBJECT') {
  1840.         $canclip = 1; $raw = 1;
  1841.         $tmp = &dehtmlize($Subject{$previndex});
  1842.         last REPLACESW;
  1843.     }
  1844.         if ($var eq 'PROG')
  1845.         { $tmp = $PROG; last REPLACESW; }
  1846.         if ($var eq 'TIDXFNAME')
  1847.         { $tmp = $TIDXNAME; last REPLACESW; }
  1848.         if ($var eq 'TIDXTITLE')
  1849.         { $canclip = 1; $tmp = $TTITLE; last REPLACESW; }
  1850.         if ($var eq 'VERSION')
  1851.         { $tmp = $VERSION; last REPLACESW; }
  1852.         if ($var eq '')
  1853.         { $tmp = '$'; last REPLACESW; }
  1854.     if ($var eq 'NEXTBUTTON') {
  1855.         $expand = 1;
  1856.         $tmp = (($i < $maxnum) ? $NEXTBUTTON : $NEXTBUTTONIA);
  1857.         last REPLACESW;
  1858.     }
  1859.     if ($var eq 'NEXTLINK') {
  1860.         $expand = 1;
  1861.         $tmp = (($i < $maxnum) ? $NEXTLINK : $NEXTLINKIA);
  1862.         last REPLACESW;
  1863.     }
  1864.     if ($var eq 'PREVBUTTON') {
  1865.         $expand = 1;
  1866.         $tmp = (($i > 0) ? $PREVBUTTON : $PREVBUTTONIA);
  1867.         last REPLACESW;
  1868.     }
  1869.     if ($var eq 'PREVLINK') {
  1870.         $expand = 1;
  1871.         $tmp = (($i > 0) ? $PREVLINK : $PREVLINKIA);
  1872.         last REPLACESW;
  1873.     }
  1874.         if ($var eq 'YYMMDD') {
  1875.         $tmp = &time2mmddyy((split(/$X/o, $index))[0], 'yymmdd');
  1876.         last REPLACESW;
  1877.     }
  1878.     warn qq|Warning: Unrecognized variable: "$val"\n|;
  1879.     return '';
  1880.     }
  1881.  
  1882.     ##    Check if string needs to expanded again
  1883.     if ($expand) {
  1884.     $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  1885.     }
  1886.  
  1887.     ##    Check if clipping string
  1888.     if ($len > 0 && $canclip) {
  1889.     $ret = substr($tmp, 0, $len);
  1890.     } else {
  1891.     $ret = $tmp;
  1892.     }
  1893.  
  1894.     ##    Check if URL text specifier is set
  1895.     if ($isurl) {
  1896.     $ret = &urlize($ret);
  1897.     } else {
  1898.     &htmlize(*ret)  if $raw;
  1899.     }
  1900.  
  1901.     ##    Check for subject link
  1902.     $ret = qq|<A NAME="$i_p0" HREF="$filename">$ret</A>|  if $var eq 'SUBJECT';
  1903.  
  1904.     $ret;
  1905. }
  1906. ##---------------------------------------------------------------------------
  1907. ##    Add mailto URLs to $str.
  1908. ##
  1909. sub mailto {
  1910.     local(*str) = shift;
  1911.     if ($MAILTOURL) {
  1912.     $str =~ s|([\!\%\w\.\-+=]+@[\w\.\-]+)|&mailUrl($1)|ge;
  1913.     } else {
  1914.     $str =~ s|([\!\%\w\.\-+=]+@[\w\.\-]+)|<A HREF="mailto:$1">$1</A>|g;
  1915.     }
  1916. }
  1917. ##---------------------------------------------------------------------------
  1918. ##    $sub, $msgid, $from come from read_mail_header() (ugly!!!!)
  1919. ##
  1920. sub mailUrl {
  1921.     local($to) = (&urlize(shift));
  1922.     local($url) = ($MAILTOURL);
  1923.     local($subjectl, $froml, $msgidl) =
  1924.      (&urlize($sub), &urlize($from), &urlize($msgid));
  1925.     $url =~ s/\$FROM\$/$froml/g;
  1926.     $url =~ s/\$MSGID\$/$msgidl/g;
  1927.     $url =~ s/\$SUBJECT\$/$subjectl/g;
  1928.     $url =~ s/\$SUBJECTNA\$/$subjectl/g;
  1929.     $url =~ s/\$TO\$/$to/g;
  1930.     qq|<A HREF="$url">$to</A>|;
  1931. }
  1932. ##---------------------------------------------------------------------------
  1933. sub newsurl {
  1934.     local(*str) = shift;
  1935.     local($h, @groups);
  1936.     $str =~ s/^([^:]*:\s*)//;  $h = $1;
  1937.     $str =~ s/\s//g;            # Strip whitespace
  1938.     @groups = split(/,/, $str);        # Split groups
  1939.     foreach (@groups) {            # Make hyperlinks
  1940.     s|(.*)|<A HREF="news:$1">$1</A>|;
  1941.     }
  1942.     $str = $h . join(', ', @groups);    # Rejoin string
  1943. }
  1944. ##---------------------------------------------------------------------------
  1945. sub get_header_tags {
  1946.     local($f) = shift;
  1947.     local($ftago, $ftagc, $tago, $tagc);
  1948.  
  1949.     ## Get user specified tags (this is one funcky looking code)
  1950.     $tag = (defined($HeadHeads{$f}) ?
  1951.         $HeadHeads{$f} : $HeadHeads{"-default-"});
  1952.     $ftag = (defined($HeadFields{$f}) ?
  1953.          $HeadFields{$f} : $HeadFields{"-default-"});
  1954.     if ($tag) { $tago = "<$tag>";  $tagc = "</$tag>"; }
  1955.     else { $tago = $tagc = ''; }
  1956.     if ($ftag) { $ftago = "<$ftag>";  $ftagc = "</$ftag>"; }
  1957.     else { $ftago = $ftagc = ''; }
  1958.  
  1959.     ($tago, $tagc, $ftago, $ftagc);
  1960. }
  1961. ##---------------------------------------------------------------------------
  1962. sub field_add_links {
  1963.     local($label, *fld_text) = @_;
  1964.     &mailto(*fld_text)
  1965.     if !$NOMAILTO &&
  1966.         $label =~ /^(to|from|cc|sender|reply-to)/i;
  1967.     &newsurl(*fld_text)
  1968.     if !$NONEWS && $label =~ /^newsgroup/i;
  1969.  
  1970. }
  1971. ##---------------------------------------------------------------------------
  1972. ##    convert_line() translates a line to HTML.  Checks are made for
  1973. ##    embedded URLs.
  1974. ##
  1975. sub convert_line {
  1976.     local($str) = $_[0];
  1977.     local($item, $item2, $item2h, @array);
  1978.  
  1979.     if (!$NOURL &&
  1980.     (@array = split(m%($Url[^\s\(\)\|<>"']*[^\.\?;,"'\|\[\]\(\)\s<>])%o,
  1981.           $str))
  1982.        ) {
  1983.         $str = '';
  1984.         while($#array > 0) {
  1985.         $item = &entify(shift @array);      # Get non-URL text
  1986.         $item2 = shift @array;              # Get URL
  1987.         $item2h = &entify($item2);          # Variable for <A> content
  1988.  
  1989.         $str .= join('',
  1990.                  $item,
  1991.                  '<A HREF="', $item2, '">', $item2h, '</A>');
  1992.  
  1993.         # The next line is needed since Perl's split function also
  1994.         # returns extra entries for nested ()'s in the split pattern.
  1995.         shift @array  if $array[0] =~ m%^$Url$%o;
  1996.         }
  1997.         $item = &entify(shift @array);          # Last item in array
  1998.         $str .= $item;
  1999.     } else {
  2000.     &htmlize(*str);
  2001.     }
  2002.     $str;
  2003. }
  2004. ##---------------------------------------------------------------------------
  2005. ##    ign_signals() sets mhonarc to ignore termination signals.  This
  2006. ##    routine is called right before an archive is written/editted to
  2007. ##    help prevent archive corruption.
  2008. ##
  2009. sub ign_signals {
  2010.     $SIG{'ABRT'} = 'IGNORE';
  2011.     $SIG{'HUP'}  = 'IGNORE';
  2012.     $SIG{'INT'}  = 'IGNORE';
  2013.     $SIG{'PIPE'} = 'IGNORE';
  2014.     $SIG{'QUIT'} = 'IGNORE';
  2015.     $SIG{'TERM'} = 'IGNORE';
  2016. }
  2017. ##---------------------------------------------------------------------------
  2018. ##    set_handler() sets up the quit() routine to be called when
  2019. ##    a termination signal is sent to mhonarc.
  2020. sub set_handler {
  2021.     $SIG{'ABRT'} = 'quit';
  2022.     $SIG{'HUP'}  = 'quit';
  2023.     $SIG{'INT'}  = 'quit';
  2024.     $SIG{'PIPE'} = 'quit';
  2025.     $SIG{'QUIT'} = 'quit';
  2026.     $SIG{'TERM'} = 'quit';
  2027. }
  2028. ##---------------------------------------------------------------------------
  2029. ##    create_lock_file() creates a file with zero permissions to act
  2030. ##    as a lock.  Thanks to Walter_Hobbs@rand.org (Walt Hobbs) for
  2031. ##    giving me a way to achieve this in Perl without possible race
  2032. ##    conditions or the use of syscall.
  2033. ##
  2034. ##    Note: There is yet to be a way to a single locking capability
  2035. ##    that works across mutliple operating systems: Unix, DOS, etc.
  2036. ##
  2037. sub create_lock_file {
  2038.     local($file, $tries, $sleep, $force) = @_;
  2039.     local($umask, $ret);
  2040.     $ret = 0;
  2041.     eval '$umask = umask(0777)'  if $UNIX;
  2042.     while ($tries > 0) {
  2043.     if (open(LCK_FILE, "> $file")) {
  2044.         $ISLOCK = 1;
  2045.         $ret = 1;
  2046.         last;
  2047.     }
  2048.     sleep($sleep)  if $sleep > 0;
  2049.     $tries--;
  2050.     }
  2051.     if ($force) {        # Set lock files if force option set
  2052.     $ISLOCK = 1;  $ret = 1;
  2053.     }
  2054.     eval 'umask($umask)'  if $UNIX;
  2055.     $ret;
  2056. }
  2057. ##---------------------------------------------------------------------------
  2058. sub clean_up {
  2059.     if ($ISLOCK) {
  2060.     unlink ($LOCKFILE);
  2061.     $ISLOCK = 0;
  2062.     }
  2063. }
  2064. ##---------------------------------------------------------------------------
  2065. sub error {
  2066.     &clean_up();
  2067.     die @_, "\n";
  2068. }
  2069. ##---------------------------------------------------------------------------
  2070. sub quit {
  2071.     local($status) = shift;
  2072.     &clean_up();
  2073.     if ($TIME) {
  2074.     $EndTime = (times)[0];
  2075.     printf(STDERR "\nTime: %.4f CPU seconds\n", $EndTime - $StartTime);
  2076.     }
  2077.     exit $status;
  2078. }
  2079. ##---------------------------------------------------------------------------
  2080. ##    Create HTML for header
  2081. sub htmlize_header {
  2082.     local(*fields, *l2o) = @_;
  2083.     local($tmp, $key, $tago, $tagc, $ftago, $ftagc, $mesg, $item, @array, %hf);
  2084.     %hf = %fields;
  2085.     foreach $item (@FieldOrder) {
  2086.     if ($item eq '-extra-') {
  2087.         foreach $key (sort keys %hf) {
  2088.         next  if $FieldODefs{$key};
  2089.         delete $hf{$key}, next  if &exclude_field($key);
  2090.  
  2091.         @array = split(/$'FieldSep/o, $hf{$key});
  2092.         foreach $tmp (@array) {
  2093.             $tmp = &convert_line($tmp);
  2094.             &field_add_links($key, *tmp);
  2095.             ($tago, $tagc, $ftago, $ftagc) = &get_header_tags($key);
  2096.             $mesg .= join('', "<LI>\n",
  2097.                   $tago, $l2o{$key}, $tagc, ": ",
  2098.                   $ftago, $tmp, $ftagc, "\n",
  2099.                   "</LI>\n");
  2100.         }
  2101.         delete $hf{$key};
  2102.         }
  2103.     } else {
  2104.         if (!&exclude_field($item) && $hf{$item}) {
  2105.         @array = (split(/$'FieldSep/o, $hf{$item}));
  2106.         foreach $tmp (@array) {
  2107.             $tmp = &convert_line($tmp);
  2108.             &field_add_links($item, *tmp);
  2109.             ($tago, $tagc, $ftago, $ftagc) = &get_header_tags($item);
  2110.             $mesg .= join('', "<LI>\n",
  2111.                   $tago, $l2o{$item}, $tagc, ": ",
  2112.                   $ftago, $tmp, $ftagc, "\n",
  2113.                   "</LI>\n");
  2114.         }
  2115.         }
  2116.         delete $hf{$item};
  2117.     }
  2118.     }
  2119.     if ($mesg) { $mesg = "<UL>\n" . $mesg . "</UL>\n"; }
  2120.     $mesg;
  2121. }
  2122. ##---------------------------------------------------------------------------
  2123. sub output_thread_index {
  2124.     local($handle) = $_[0];
  2125.     local(%HasRef, %Replies, %Printed);
  2126.     local(@array, @refs);
  2127.     local($index, $msgid, $refindex, $level);
  2128.  
  2129.     ## Routine to print thread
  2130.     ##
  2131.     sub print_thread {
  2132.     local($i) = @_;
  2133.     local(@repls);
  2134.  
  2135.     &print_thread_entry($handle, $i);
  2136.     $Printed{$i} = 1;
  2137.     if (@repls = sort increase_index split(/$bs/o, $Replies{$i})) {
  2138.         $level++;
  2139.         print $handle "<UL>\n"  if $level <= $TLEVELS;
  2140.         foreach (@repls) {
  2141.         &print_thread($_);
  2142.         }
  2143.         print $handle "</UL>\n"  if $level <= $TLEVELS;
  2144.         $level--;
  2145.     }
  2146.     }
  2147.  
  2148.     ## Compute threads
  2149.     ##
  2150.     foreach $index (keys %Subject) {
  2151.     next  unless $Refs{$index};
  2152.     @refs = split(/$X/o, $Refs{$index});
  2153.     $msgid = $refs[$#refs];        ## get last (rfc1036)
  2154.     if ($refindex = $MsgId{$msgid}) {
  2155.         $HasRef{$index} = 1;
  2156.         if ($Replies{$refindex}) {
  2157.         $Replies{$refindex} .= $bs . $index;
  2158.         } else {
  2159.         $Replies{$refindex} = $index;
  2160.         }
  2161.     }
  2162.     }
  2163.  
  2164.     ## Print index
  2165.     ##
  2166.     if ($TREVERSE) {
  2167.     @array = sort decrease_index keys %Subject;
  2168.     } else {
  2169.     @array = sort increase_index keys %Subject;
  2170.     }
  2171.     #    Set messages that are shown in index
  2172.     if ($IDXSIZE && (($i = ($#array+1) - $IDXSIZE) > 0)) {
  2173.     if ($TREVERSE) {
  2174.         splice(@array, $IDXSIZE);
  2175.     } else {
  2176.         splice(@array, 0, $i);
  2177.     }
  2178.     }
  2179.     if ($TSUBSORT) {
  2180.     @array = sort increase_subject @array;
  2181.     }
  2182.     print $handle "<UL>\n";
  2183.     foreach $index (@array) {
  2184.     &print_thread($index) unless $Printed{$index} || $HasRef{$index};
  2185.     }
  2186.     print $handle "</UL>\n";
  2187. }
  2188. ##---------------------------------------------------------------------------
  2189. sub print_thread_entry {
  2190.     local($handle, $index) = @_;
  2191.     local($i_p0, $filename, $tmpl, $msgnum);
  2192.  
  2193.     $msgnum = $IndexNum{$index};
  2194.     $i_p0 = &fmt_msgnum($msgnum);        # Var for replace_li_var
  2195.     $filename = &msgnum_filename($msgnum);    # Var for replace_li_var
  2196.  
  2197.     $tmpl = $TLITXT;
  2198.     $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
  2199.     print $handle "<LI>", $tmpl, "</LI>\n";
  2200. }
  2201. ##---------------------------------------------------------------------------
  2202. ##    Create Index2MsgId if not defined
  2203. ##
  2204. sub defineIndex2MsgId {
  2205.     if (!defined(%Index2MsgId)) {
  2206.     foreach (keys %MsgId) {
  2207.         $Index2MsgId{$MsgId{$_}} = $_;
  2208.     }
  2209.     }
  2210. }
  2211. ##---------------------------------------------------------------------------
  2212. ##    create_routines is used to dynamically create routines that
  2213. ##    would benefit from being create at run-time.  Routines
  2214. ##    that have to check against several regular expressions
  2215. ##    are candidates.
  2216. ##
  2217. sub create_routines {
  2218.     local($sub) = '';
  2219.  
  2220.     ##-----------------------------------------------------------------------
  2221.     ## exclude_field: Used to determine if field should be excluded from
  2222.     ## message header
  2223.     ##
  2224.     $sub  =<<'EndOfRoutine';
  2225.     sub exclude_field {
  2226.         local($f) = shift;
  2227.         local($pat, $ret);
  2228.         $ret = 0;
  2229.         EXC_FIELD_SW: {
  2230. EndOfRoutine
  2231.  
  2232.     # Create switch block for checking field against regular
  2233.     # expressions (an large || statement could also work).
  2234.     foreach $pat (keys %HFieldsExc) {
  2235.         $sub .= join('',
  2236.              'if ($f =~ /^',
  2237.              $pat,
  2238.              '/i) { $ret = 1;  last EXC_FIELD_SW; }',
  2239.              "\n");
  2240.     }
  2241.  
  2242.     $sub .=<<'EndOfRoutine';
  2243.         }
  2244.         $ret;
  2245.     }
  2246. EndOfRoutine
  2247.  
  2248.     eval $sub;
  2249.     &error("ERROR: Unable to create exclude_field routine:\n\t$@") if $@;
  2250. }
  2251. ##---------------------------------------------------------------------------
  2252. ##    Usage routine
  2253. ##
  2254. sub usage {
  2255.     select(STDOUT);
  2256.     print <<EndOfUsage;
  2257. Usage:  $PROG [<options>] <file> ... 
  2258.         $PROG [<options>] -rmm <msg #> ...
  2259. Options:
  2260.   -add                  : Add message(s) to archive
  2261.   -dbfile <name>        : Name of MHonArc database file
  2262.                             (def: ".mhonarc.db")
  2263.   -docurl <url>         : URL to MHonArc documentation
  2264.                             (def: "http://www.oac.uci.edu/indiv/ehood/
  2265.                                    mhonarc.html")
  2266.   -editidx              : Only edit/change index page and messages
  2267.   -force                : Perform archive operation even if unable to lock
  2268.   -footer <file>        : File containing user text for bottom of index page
  2269.   -genidx               : Output index to stdout based upon archive contents
  2270.   -header <file>        : User text to include at top of index page
  2271.   -help                 : This message
  2272.   -idxfname <name>      : Name of index page
  2273.                             (def: "maillist.html")
  2274.   -idxsize <#>          : Maximum number of messages shown in indexes
  2275.   -lockdelay <#>        : Time delay, in seconds, between lock tries
  2276.                             (def: "3")
  2277.   -locktries <#>        : Maximum number of tries in locking an archive
  2278.                             (def: "10")
  2279.   -mailtourl <url>      : URL to use for e-mail address hyperlinks
  2280.                             (def: "mailto:\$TO\$")
  2281.   -maxsize <#>          : Maximum number of messages allowed in archive
  2282.   -msgsep <exp>         : Message separator expression for mailbox files
  2283.                             (def: "^From ")
  2284.   -nodoc                : Do not print link to doc at end of index page
  2285.   -nomailto             : Do not add in mailto links for e-mail addresses
  2286.   -nonews               : Do not add links to newsgroups
  2287.   -noreverse            : List messages in normal order
  2288.   -nosort               : Do not sort messages
  2289.   -nothread             : Do not create threaded index
  2290.   -notsubsort           : Do not sort threads by subject
  2291.   -outdir <path>        : Destination/location of HTML mail archive
  2292.                             (def: ".")
  2293.   -quiet                : Suppress status messages during execution
  2294.   -rcfile <file>        : Resource file for MHonArc
  2295.   -reverse              : List messages in reverse order
  2296.   -rmm                  : Remove messages from archive
  2297.   -savemem              : Write message data while processing
  2298.   -scan                 : List out archive contents to stdout
  2299.   -single               : Convert a single message to HTML
  2300.   -sort                 : Sort by dates (this is the default)
  2301.   -subsort              : Sort message by subject
  2302.   -thread               : Create threaded index
  2303.   -tidxfname <name>     : File name of threaded index page
  2304.                             (def: "threads.html")
  2305.   -time                 : Print to stderr CPU time used to process mail
  2306.   -title <string>       : Title of main index page
  2307.                             (def: "Mail Index")
  2308.   -tlevels <#>          : Maximum # of nested lists in threaded index
  2309.                             (def: "3")
  2310.   -treverse             : List threads with newest thread first
  2311.   -tsubsort             : Sort threads by subject
  2312.   -ttitle <string>      : Title of thread index page
  2313.                             (def: "Mail Thread Index")
  2314.   -umask <umask>        : Umask of MHonArc process
  2315.  
  2316. Description:
  2317.   MHonArc is a highly customizable Perl program for converting e-mail into
  2318.   HTML.  MHonArc will convert UUCP style mailbox files or MH mail folders
  2319.   into HTML with an index linking to each mail message.  The -single option
  2320.   can be used to convert a single mail message.
  2321.  
  2322.   Read the documentation for more complete usage information.
  2323.  
  2324. Version:
  2325.   $VERSION
  2326.   Copyright (C) 1995,1996  Earl Hood, ehood\@convex.com
  2327.   MHonArc comes with ABSOLUTELY NO WARRANTY and MHonArc may be copied only
  2328.   under the terms of the GNU General Public License, which may be found in
  2329.   the MHonArc distribution.
  2330.  
  2331. EndOfUsage
  2332.     exit 0;
  2333. }
  2334.